671 lines
17 KiB
Markdown
671 lines
17 KiB
Markdown
# Clean Architecture
|
||
|
||
## 目录
|
||
- [Clean Architecture概念](#clean-architecture概念)
|
||
- [分层架构](#分层架构)
|
||
- [依赖规则](#依赖规则)
|
||
- [领域层](#领域层)
|
||
- [数据层](#数据层)
|
||
- [表现层](#表现层)
|
||
- [Clean Architecture实现](#clean-architecture实现)
|
||
- [Clean Architecture最佳实践](#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 Sources(API、Database、Cache)
|
||
- Data Models
|
||
|
||
### 依赖方向
|
||
|
||
```
|
||
表现层 → 领域层 → 数据层
|
||
```
|
||
|
||
- 表现层依赖领域层
|
||
- 领域层不依赖表现层和数据层
|
||
- 数据层实现领域层的接口
|
||
|
||
---
|
||
|
||
## 依赖规则
|
||
|
||
### 依赖倒置原则
|
||
|
||
```java
|
||
// ✅ 正确:领域层定义接口,数据层实现接口
|
||
// 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(实体)
|
||
|
||
```java
|
||
// 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(用例)
|
||
|
||
```java
|
||
// 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(仓库接口)
|
||
|
||
```java
|
||
// 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(仓库实现)
|
||
|
||
```java
|
||
// 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(数据源)
|
||
|
||
```java
|
||
// 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
|
||
|
||
```java
|
||
// 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
|
||
|
||
```java
|
||
// 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/
|
||
```
|
||
|
||
### 依赖配置
|
||
|
||
```gradle
|
||
// 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. 依赖倒置
|
||
|
||
```java
|
||
// ✅ 正确:领域层定义接口
|
||
// 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. 单一职责
|
||
|
||
```java
|
||
// ✅ 正确:每个类只有一个职责
|
||
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. 接口隔离
|
||
|
||
```java
|
||
// ✅ 正确:接口职责单一
|
||
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. 测试
|
||
|
||
```java
|
||
// 领域层可以独立测试
|
||
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年*
|