Files
mkdocs/docs/android面试/系统架构/Clean_Architecture.md
2026-01-15 11:53:37 +08:00

17 KiB
Raw Blame History

Clean Architecture

目录


Clean Architecture概念

什么是 Clean Architecture

Clean Architecture整洁架构是由 Robert C. Martin 提出的一种软件架构设计理念,强调:

  1. 独立性框架、UI、数据库、外部服务都是可替换的
  2. 可测试性:业务逻辑可以独立测试
  3. 独立性:业务逻辑不依赖外部框架
  4. 可维护性:代码结构清晰,易于维护

Clean Architecture 架构图

┌─────────────────────────────────────┐
│          Presentation Layer          │  ← 表现层UI、ViewModel
│  (Activities, Fragments, ViewModels) │
└──────────────┬──────────────────────┘
               │
┌──────────────▼──────────────────────┐
│           Domain Layer               │  ← 领域层(业务逻辑)
│  (Use Cases, Entities, Interfaces)  │
└──────────────┬──────────────────────┘
               │
┌──────────────▼──────────────────────┐
│            Data Layer               │  ← 数据层Repository 实现)
│  (Repositories, Data Sources, API)  │
└─────────────────────────────────────┘

Clean Architecture 核心原则

  1. 依赖倒置:依赖方向由外向内
  2. 单一职责:每个类只有一个职责
  3. 开闭原则:对扩展开放,对修改关闭
  4. 接口隔离:使用接口隔离依赖

分层架构

三层架构

1. 表现层Presentation Layer

职责:

  • 显示 UI
  • 处理用户输入
  • 调用 Use Case

组件:

  • Activities
  • Fragments
  • ViewModels
  • Adapters

2. 领域层Domain Layer

职责:

  • 业务逻辑
  • 业务规则
  • 实体定义

组件:

  • Entities实体
  • Use Cases用例
  • Repository Interfaces仓库接口

3. 数据层Data Layer

职责:

  • 数据获取
  • 数据存储
  • Repository 实现

组件:

  • Repository Implementations
  • Data SourcesAPI、Database、Cache
  • Data Models

依赖方向

表现层 → 领域层 → 数据层
  • 表现层依赖领域层
  • 领域层不依赖表现层和数据层
  • 数据层实现领域层的接口

依赖规则

依赖倒置原则

// ✅ 正确:领域层定义接口,数据层实现接口
// Domain Layer
public interface UserRepository {
    Single<List<User>> getUsers();
}

// Data Layer
public class UserRepositoryImpl implements UserRepository {
    @Override
    public Single<List<User>> getUsers() {
        // 实现
    }
}

// ❌ 错误:领域层依赖数据层
// Domain Layer
public class UserUseCase {
    private UserRepositoryImpl repository; // 依赖具体实现
}

依赖方向

┌─────────────┐
│ Presentation │
└──────┬───────┘
       │ 依赖
       ↓
┌─────────────┐
│   Domain    │  ← 核心层,不依赖其他层
└──────┬───────┘
       │ 接口
       ↑ 实现
┌─────────────┐
│    Data     │
└─────────────┘

领域层

Entity实体

// Domain Layer: Entity
public class User {
    private String id;
    private String name;
    private String email;
    
    public User(String id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    // Getters
    public String getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
    
    // 业务逻辑
    public boolean isValid() {
        return name != null && !name.isEmpty() 
            && email != null && email.contains("@");
    }
}

Use Case用例

// Domain Layer: Use Case
public class GetUsersUseCase {
    private UserRepository repository;
    
    public GetUsersUseCase(UserRepository repository) {
        this.repository = repository;
    }
    
    public Single<List<User>> execute() {
        return repository.getUsers()
            .map(users -> {
                // 业务逻辑处理
                return users.stream()
                    .filter(User::isValid)
                    .collect(Collectors.toList());
            });
    }
}

public class AddUserUseCase {
    private UserRepository repository;
    
    public AddUserUseCase(UserRepository repository) {
        this.repository = repository;
    }
    
    public Completable execute(User user) {
        if (!user.isValid()) {
            return Completable.error(new IllegalArgumentException("Invalid user"));
        }
        return repository.addUser(user);
    }
}

Repository Interface仓库接口

// Domain Layer: Repository Interface
public interface UserRepository {
    Single<List<User>> getUsers();
    Single<User> getUserById(String id);
    Completable addUser(User user);
    Completable updateUser(User user);
    Completable deleteUser(String id);
}

数据层

Repository Implementation仓库实现

// Data Layer: Repository Implementation
public class UserRepositoryImpl implements UserRepository {
    private UserRemoteDataSource remoteDataSource;
    private UserLocalDataSource localDataSource;
    
    public UserRepositoryImpl(
        UserRemoteDataSource remoteDataSource,
        UserLocalDataSource localDataSource
    ) {
        this.remoteDataSource = remoteDataSource;
        this.localDataSource = localDataSource;
    }
    
    @Override
    public Single<List<User>> getUsers() {
        return remoteDataSource.getUsers()
            .doOnSuccess(localDataSource::saveUsers)
            .onErrorResumeNext(localDataSource.getUsers());
    }
    
    @Override
    public Single<User> getUserById(String id) {
        return localDataSource.getUserById(id)
            .onErrorResumeNext(remoteDataSource.getUserById(id));
    }
    
    @Override
    public Completable addUser(User user) {
        return remoteDataSource.addUser(user)
            .andThen(localDataSource.saveUser(user));
    }
    
    @Override
    public Completable updateUser(User user) {
        return remoteDataSource.updateUser(user)
            .andThen(localDataSource.updateUser(user));
    }
    
    @Override
    public Completable deleteUser(String id) {
        return remoteDataSource.deleteUser(id)
            .andThen(localDataSource.deleteUser(id));
    }
}

Data Source数据源

// Data Layer: Remote Data Source
public class UserRemoteDataSource {
    private ApiService apiService;
    
    public UserRemoteDataSource(ApiService apiService) {
        this.apiService = apiService;
    }
    
    public Single<List<User>> getUsers() {
        return apiService.getUsers()
            .map(userResponses -> {
                return userResponses.stream()
                    .map(this::mapToUser)
                    .collect(Collectors.toList());
            });
    }
    
    private User mapToUser(UserResponse response) {
        return new User(
            response.getId(),
            response.getName(),
            response.getEmail()
        );
    }
}

// Data Layer: Local Data Source
public class UserLocalDataSource {
    private UserDao userDao;
    
    public UserLocalDataSource(UserDao userDao) {
        this.userDao = userDao;
    }
    
    public Single<List<User>> getUsers() {
        return userDao.getUsers()
            .map(userEntities -> {
                return userEntities.stream()
                    .map(this::mapToUser)
                    .collect(Collectors.toList());
            });
    }
    
    public void saveUsers(List<User> users) {
        List<UserEntity> entities = users.stream()
            .map(this::mapToEntity)
            .collect(Collectors.toList());
        userDao.insertUsers(entities);
    }
    
    private User mapToUser(UserEntity entity) {
        return new User(
            entity.getId(),
            entity.getName(),
            entity.getEmail()
        );
    }
    
    private UserEntity mapToEntity(User user) {
        UserEntity entity = new UserEntity();
        entity.setId(user.getId());
        entity.setName(user.getName());
        entity.setEmail(user.getEmail());
        return entity;
    }
}

表现层

ViewModel

// Presentation Layer: ViewModel
public class UserViewModel extends ViewModel {
    private GetUsersUseCase getUsersUseCase;
    private AddUserUseCase addUserUseCase;
    private MutableLiveData<List<User>> users = new MutableLiveData<>();
    private MutableLiveData<String> error = new MutableLiveData<>();
    private MutableLiveData<Boolean> loading = new MutableLiveData<>();
    
    public UserViewModel(
        GetUsersUseCase getUsersUseCase,
        AddUserUseCase addUserUseCase
    ) {
        this.getUsersUseCase = getUsersUseCase;
        this.addUserUseCase = addUserUseCase;
    }
    
    public LiveData<List<User>> getUsers() {
        return users;
    }
    
    public LiveData<String> getError() {
        return error;
    }
    
    public LiveData<Boolean> getLoading() {
        return loading;
    }
    
    public void loadUsers() {
        loading.setValue(true);
        getUsersUseCase.execute()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                userList -> {
                    loading.setValue(false);
                    users.setValue(userList);
                },
                throwable -> {
                    loading.setValue(false);
                    error.setValue(throwable.getMessage());
                }
            );
    }
    
    public void addUser(String name, String email) {
        loading.setValue(true);
        User user = new User(UUID.randomUUID().toString(), name, email);
        addUserUseCase.execute(user)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                () -> {
                    loading.setValue(false);
                    loadUsers();
                },
                throwable -> {
                    loading.setValue(false);
                    error.setValue(throwable.getMessage());
                }
            );
    }
}

Activity/Fragment

// Presentation Layer: Activity
public class UserActivity extends AppCompatActivity {
    private UserViewModel viewModel;
    private ActivityUserBinding binding;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_user);
        
        // 依赖注入
        UserRepository repository = new UserRepositoryImpl(
            new UserRemoteDataSource(createApiService()),
            new UserLocalDataSource(createUserDao())
        );
        
        GetUsersUseCase getUsersUseCase = new GetUsersUseCase(repository);
        AddUserUseCase addUserUseCase = new AddUserUseCase(repository);
        
        viewModel = ViewModelProviders.of(this, new ViewModelFactory(
            getUsersUseCase,
            addUserUseCase
        )).get(UserViewModel.class);
        
        binding.setViewModel(viewModel);
        binding.setLifecycleOwner(this);
        
        observeViewModel();
    }
    
    private void observeViewModel() {
        viewModel.getUsers().observe(this, users -> {
            adapter.updateUsers(users);
        });
        
        viewModel.getError().observe(this, error -> {
            Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
        });
    }
}

Clean Architecture实现

项目结构

app/
├── presentation/           # 表现层
│   ├── ui/
│   │   ├── activities/
│   │   ├── fragments/
│   │   └── adapters/
│   └── viewmodels/
├── domain/                 # 领域层
│   ├── entities/
│   ├── usecases/
│   └── repositories/
└── data/                   # 数据层
    ├── repositories/
    ├── datasources/
    │   ├── remote/
    │   └── local/
    └── models/

依赖配置

// app/build.gradle
dependencies {
    // 表现层依赖领域层
    implementation project(':domain')
    
    // 表现层依赖数据层(用于依赖注入)
    implementation project(':data')
}

// domain/build.gradle
dependencies {
    // 领域层不依赖其他层,只依赖 RxJava 等工具库
    implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
}

// data/build.gradle
dependencies {
    // 数据层依赖领域层(实现接口)
    implementation project(':domain')
    
    // 数据层依赖第三方库
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'androidx.room:room-runtime:2.3.0'
}

Clean Architecture最佳实践

1. 依赖倒置

// ✅ 正确:领域层定义接口
// Domain Layer
public interface UserRepository {
    Single<List<User>> getUsers();
}

// Data Layer 实现接口
public class UserRepositoryImpl implements UserRepository {
    // 实现
}

// ❌ 错误:领域层依赖数据层
// Domain Layer
public class UserUseCase {
    private UserRepositoryImpl repository; // 依赖具体实现
}

2. 单一职责

// ✅ 正确:每个类只有一个职责
public class GetUsersUseCase {
    public Single<List<User>> execute() {
        // 只负责获取用户列表
    }
}

public class AddUserUseCase {
    public Completable execute(User user) {
        // 只负责添加用户
    }
}

// ❌ 错误:一个类承担多个职责
public class UserUseCase {
    public void getUsers() { }
    public void addUser() { }
    public void updateUser() { }
    public void deleteUser() { }
}

3. 接口隔离

// ✅ 正确:接口职责单一
public interface UserRepository {
    Single<List<User>> getUsers();
    Single<User> getUserById(String id);
}

// ❌ 错误:接口职责过多
public interface UserRepository {
    void getUsers();
    void addUser();
    void updateUser();
    void deleteUser();
    void uploadFile();
    void downloadFile();
}

4. 测试

// 领域层可以独立测试
public class GetUsersUseCaseTest {
    @Test
    public void testExecute() {
        UserRepository mockRepository = mock(UserRepository.class);
        when(mockRepository.getUsers()).thenReturn(Single.just(users));
        
        GetUsersUseCase useCase = new GetUsersUseCase(mockRepository);
        useCase.execute()
            .test()
            .assertValue(users);
    }
}

面试常见问题

Q1: 什么是 Clean Architecture

答案: Clean Architecture整洁架构是由 Robert C. Martin 提出的一种软件架构设计理念,强调:

  1. 独立性框架、UI、数据库都是可替换的
  2. 可测试性:业务逻辑可以独立测试
  3. 独立性:业务逻辑不依赖外部框架
  4. 可维护性:代码结构清晰,易于维护

Q2: Clean Architecture 的分层?

答案:

  1. 表现层UI、ViewModel、Activities、Fragments
  2. 领域层Entities、Use Cases、Repository Interfaces
  3. 数据层Repository Implementations、Data Sources

Q3: Clean Architecture 的依赖规则?

答案:

  1. 依赖倒置:领域层定义接口,数据层实现接口
  2. 依赖方向:表现层 → 领域层 → 数据层
  3. 领域层独立:领域层不依赖其他层

Q4: Use Case 的作用?

答案: Use Case用例封装了特定的业务逻辑每个 Use Case 只负责一个业务功能,使业务逻辑清晰、可测试、可复用。

Q5: Repository 模式的作用?

答案: Repository 模式封装了数据访问逻辑,提供统一的数据访问接口,隐藏了数据来源(网络、数据库、缓存)的细节。

Q6: Clean Architecture 的优势?

答案:

  1. 可测试性:业务逻辑可以独立测试
  2. 可维护性:代码结构清晰
  3. 可扩展性:易于添加新功能
  4. 独立性:不依赖外部框架

总结

Clean Architecture 是一种优秀的软件架构设计理念,通过分层架构和依赖倒置,实现了业务逻辑的独立性和可测试性。在实际项目中,需要合理划分层次、定义接口、实现依赖倒置。


最后更新2024年