17 KiB
17 KiB
Clean Architecture
目录
Clean Architecture概念
什么是 Clean Architecture?
Clean Architecture(整洁架构)是由 Robert C. Martin 提出的一种软件架构设计理念,强调:
- 独立性:框架、UI、数据库、外部服务都是可替换的
- 可测试性:业务逻辑可以独立测试
- 独立性:业务逻辑不依赖外部框架
- 可维护性:代码结构清晰,易于维护
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. 表现层(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
依赖方向
表现层 → 领域层 → 数据层
- 表现层依赖领域层
- 领域层不依赖表现层和数据层
- 数据层实现领域层的接口
依赖规则
依赖倒置原则
// ✅ 正确:领域层定义接口,数据层实现接口
// 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 提出的一种软件架构设计理念,强调:
- 独立性:框架、UI、数据库都是可替换的
- 可测试性:业务逻辑可以独立测试
- 独立性:业务逻辑不依赖外部框架
- 可维护性:代码结构清晰,易于维护
Q2: Clean Architecture 的分层?
答案:
- 表现层:UI、ViewModel、Activities、Fragments
- 领域层:Entities、Use Cases、Repository Interfaces
- 数据层:Repository Implementations、Data Sources
Q3: Clean Architecture 的依赖规则?
答案:
- 依赖倒置:领域层定义接口,数据层实现接口
- 依赖方向:表现层 → 领域层 → 数据层
- 领域层独立:领域层不依赖其他层
Q4: Use Case 的作用?
答案: Use Case(用例)封装了特定的业务逻辑,每个 Use Case 只负责一个业务功能,使业务逻辑清晰、可测试、可复用。
Q5: Repository 模式的作用?
答案: Repository 模式封装了数据访问逻辑,提供统一的数据访问接口,隐藏了数据来源(网络、数据库、缓存)的细节。
Q6: Clean Architecture 的优势?
答案:
- 可测试性:业务逻辑可以独立测试
- 可维护性:代码结构清晰
- 可扩展性:易于添加新功能
- 独立性:不依赖外部框架
总结
Clean Architecture 是一种优秀的软件架构设计理念,通过分层架构和依赖倒置,实现了业务逻辑的独立性和可测试性。在实际项目中,需要合理划分层次、定义接口、实现依赖倒置。
最后更新:2024年