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年*
|