关注点分离:提升软件设计的清晰度与灵活性_架构设计
在软件架构中,关注点分离(Separation of Concerns, SoC)是一种至关重要的设计原则,它帮助开发者构建更加模块化、灵活且易于维护的系统。通过将不同的功能和业务逻辑分离到不同的组件中,我们可以降低系统的复杂性,提高代码的可读性和可维护性。

肖哥弹架构 跟大家“弹弹” 代码设计技巧,需要代码关注

欢迎 点赞,关注,评论。

关注公号Solomon肖哥弹架构获取更多精彩内容

历史热点文章

1. 关注点分离设计图:

关注点分离:提升软件设计的清晰度与灵活性_设计原则_02

标准分层架构图描述
  1. 表示层(Presentation Layer)
    • 这一层负责处理用户界面和用户体验。
    • 它与用户直接交互,收集用户输入并展示数据。
  2. 业务逻辑层(Business Logic Layer)
    • 这一层包含应用程序的核心业务逻辑。
    • 它处理数据的加工、验证和业务规则的应用。
  3. 数据访问层(Data Access Layer)
    • 这一层负责与数据库或其他持久化存储交互。
    • 它封装了所有数据的检索、更新、保存和删除操作。
  4. 数据层(Data Layer)
    • 这一层涉及到数据模型和数据库表。
    • 它代表了数据的物理存储。
  5. 服务层(Service Layer)
    • (可选)这一层可以进一步抽象业务逻辑,为表示层和业务逻辑层提供服务。
  6. 基础设施层(Infrastructure Layer)
    • 包括硬件、网络资源、消息队列、缓存等基础设施组件。
2、关注点分离的设计哲学
  • 单一职责:每个组件只负责一个单一的功能或业务逻辑。
  • 解耦合:组件之间的依赖关系最小化,提高系统的灵活性。
3、关注点分离解决什么
  • 降低复杂性:通过分离不同的关注点,系统变得更加简洁。
  • 增强可维护性:独立的组件更容易维护和升级。
4、关注点分离的特点
  • 模块化:系统被划分为多个模块,每个模块封装特定的功能。
  • 高内聚低耦合:每个模块具有高内聚性,模块间的耦合度低。
5、关注点分离的缺点
  • 过度分离:不恰当的分离可能导致系统碎片化,增加理解和维护的难度。
  • 设计挑战:需要精心设计组件的接口和交互,以实现有效的分离。
6、关注点分离使用场景

关注点分离适用于所有需要良好结构和可维护性的软件项目,尤其是在大型和复杂的系统中。

7、关注点分离案例
7.1 博客文章发布案例

博客文章发布系统通常包含以下业务逻辑:

  1. 文章创建:允许用户输入文章的标题、内容,并提交文章。
  2. 文章存储:将文章数据持久化到数据库。
  3. 文章审核:对提交的文章进行内容审核,确保符合发布标准。
  4. 文章发布:将审核通过的文章设置为公开状态,使其对读者可见。
  5. 文章检索:允许用户根据标题、内容或作者等条件检索文章。
  6. 文章更新和删除:允许作者或管理员更新或删除文章。

代码下方有分离的依据,先看代码。

// 博客文章实体
public class BlogPost {
    private String id;
    private String title;
    private String content;
    private boolean isPublished;

    // 构造器、getter和setter省略
}

// 用户实体
public class User {
    private String id;
    private String username;
    private String permission; // 例如:ROLE_ADMIN, ROLE_USER

    // 构造器、getter和setter省略
}

// 评论实体
public class Comment {
    private String id;
    private String content;
    private String blogPostId; // 关联的文章ID

    // 构造器、getter和setter省略
}

// 通知实体
public class Notification {
    // 通知内容和相关属性
}

// 用户活动跟踪实体
public class UserActivity {
    // 用户活动详情
}

// 用户数据访问接口
public interface UserRepository {
    User findById(String id);
    void save(User user);
    // 其他用户相关数据访问方法
}

// 用户数据访问实现
public class UserRepositoryImpl implements UserRepository {
    // 使用内存存储数据库
    private Map<String, User> storage = new HashMap<>();
    
    // 实现方法...
}

// 文章数据访问接口
public interface BlogPostRepository extends JpaRepository<BlogPost, String> {
    List<BlogPost> findByKeyword(String keyword);
    // 其他文章相关数据访问方法
}

// 文章数据访问实现
public class BlogPostRepositoryImpl extends JpaRepository<BlogPost, String> {
    // 使用JPA实现数据库操作
}

// 评论数据访问接口
public interface CommentRepository extends JpaRepository<Comment, String> {
    // 评论相关数据访问方法
}

// 评论数据访问实现
public class CommentRepositoryImpl extends JpaRepository<Comment, String> {
    // 使用JPA实现数据库操作
}

// 用户服务接口
public interface UserService {
    User createUser(User user);
    void updatePermission(String userId, String permission);
    // 其他用户管理相关方法
}

// 用户服务实现
public class UserServiceImpl implements UserService {
    private UserRepository userRepository;

    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public User createUser(User user) {
        // 创建用户逻辑
        return userRepository.save(user);
    }

    @Override
    public void updatePermission(String userId, String permission) {
        // 更新用户权限逻辑
        User user = userRepository.findById(userId).orElseThrow();
        user.setPermission(permission);
        userRepository.save(user);
    }
    // 其他方法实现...
}

// 内容管理服务接口
public interface ContentService {
    BlogPost saveDraft(BlogPost blogPost);
    BlogPost publish(BlogPost blogPost);
    BlogPost update(BlogPost blogPost);
    void delete(String id);
    // 其他内容管理相关方法
}

// 内容管理服务实现
public class ContentServiceImpl implements ContentService {
    private BlogPostRepository repository;

    @Autowired
    public ContentServiceImpl(BlogPostRepository repository) {
        this.repository = repository;
    }

    @Override
    public BlogPost saveDraft(BlogPost blogPost) {
        // 保存草稿逻辑
        return repository.save(blogPost);
    }

    @Override
    public BlogPost publish(BlogPost blogPost) {
        // 发布文章逻辑
        blogPost.setPublished(true);
        return repository.save(blogPost);
    }

    @Override
    public BlogPost update(BlogPost blogPost) {
        // 更新文章逻辑
        return repository.save(blogPost);
    }

    @Override
    public void delete(String id) {
        // 删除文章逻辑
        repository.deleteById(id);
    }
    // 其他方法实现...
}

// 审核服务接口
public interface ReviewService {
    void conductReview(BlogPost blogPost) throws ReviewException;
}

// 审核服务实现
public class ReviewServiceImpl implements ReviewService {
    // 审核服务逻辑,可能包括调用外部API或复杂的业务规则
    @Override
    public void conductReview(BlogPost blogPost) throws ReviewException {
        // 审核文章内容
        if (/* 审核条件 */) {
            throw new ReviewException("Content does not meet the criteria.");
        }
    }
}

// 发布服务接口
public interface PublishService {
    BlogPost publishArticle(BlogPost blogPost) throws ReviewException;
}

// 发布服务实现
public class PublishServiceImpl implements PublishService {
    private ContentService contentService;
    private ReviewService reviewService;

    @Autowired
    public PublishServiceImpl(ContentService contentService, ReviewService reviewService) {
        this.contentService = contentService;
        this.reviewService = reviewService;
    }

    @Override
    public BlogPost publishArticle(BlogPost blogPost) throws ReviewException {
        reviewService.conductReview(blogPost);
        return contentService.publish(blogPost);
    }
}

// 互动服务接口
public interface InteractionService {
    void addComment(Comment comment);
    // 其他互动相关方法
}

// 互动服务实现
public class InteractionServiceImpl implements InteractionService {
    private CommentRepository commentRepository;

    @Autowired
    public InteractionServiceImpl(CommentRepository commentRepository) {
        this.commentRepository = commentRepository;
    }

    @Override
    public void addComment(Comment comment) {
        // 添加评论逻辑
        commentRepository.save(comment);
    }
    // 其他方法实现...
}

// 搜索服务接口
public interface SearchService {
    List<BlogPost> searchByKeyword(String keyword);
    // 其他搜索相关方法
}

// 搜索服务实现
public class SearchServiceImpl implements SearchService {
    private BlogPostRepository blogPostRepository;

    @Autowired
    public SearchServiceImpl(BlogPostRepository blogPostRepository) {
        this.blogPostRepository = blogPostRepository;
    }

    @Override
    public List<BlogPost> searchByKeyword(String keyword) {
        // 根据关键词搜索文章逻辑
        return blogPostRepository.findByKeyword(keyword);
    }
    // 其他方法实现...
}

// 通知服务接口
public interface NotificationService {
    void sendNotification(Notification notification);
    // 其他通知相关方法
}

// 通知服务实现
public class NotificationServiceImpl implements NotificationService {
    // 发送通知逻辑,可能包括邮件、短信或实时消息
    @Override
    public void sendNotification(Notification notification) {
        // 发送通知实现
    }
}

// 统计分析服务接口
public interface AnalyticsService {
    void trackUserActivity(UserActivity activity);
    // 其他统计分析相关方法
}

// 统计分析服务实现
public class AnalyticsServiceImpl implements AnalyticsService {
    // 跟踪用户活动逻辑
    @Override
    public void trackUserActivity(UserActivity activity) {
        // 记录用户行为数据
    }
}

// 自定义异常:审核不通过异常
public class ReviewException extends Exception {
    public ReviewException(String message) {
        super(message);
    }
}

// 客户端应用程序
public class BlogApplication {
    public static void main(String[] args) {
        // 依赖注入和系统启动逻辑
        // ApplicationContext context = ...;
        // Autowire services where needed

        // 发布文章流程
        try {
            BlogPost blogPost = new BlogPost(/* 属性 */);
            PublishService publishService = context.getBean(PublishService.class);
            BlogPost publishedPost = publishService.publishArticle(blogPost);
            System.out.println("Article published successfully!");
        } catch (ReviewException e) {
            System.err.println("Failed to publish article: " + e.getMessage());
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.
  • 292.
8、 博客业务范围与分离
  1. 内容管理
    • 范围:涉及文章内容的存储、呈现和维护。
    • 分离思路:内容是博客系统的核心,需要专门的服务来处理与内容相关的逻辑。
  2. 用户管理
    • 范围:包括用户账户的创建、权限分配和个人资料管理。
    • 分离思路:用户是系统交互的主体,需要一套机制来管理用户信息和权限。
  3. 审核流程
    • 范围:文章发布前的质量控制和合规性检查。
    • 分离思路:保证发布内容的质量和一致性,避免违规内容的发布。
  4. 发布控制
    • 范围:文章从草稿到发布的转换,包括版本控制和状态管理。
    • 分离思路:管理文章的生命周期,确保内容按计划发布。
  5. 互动功能
    • 范围:包括评论、点赞、分享等用户与文章的交互。
    • 分离思路:提升用户参与度,增强社区互动。
  6. 搜索与导航
    • 范围:允许用户通过关键词、分类、标签等方式检索文章。
    • 分离思路:帮助用户快速找到感兴趣的内容。
  7. 通知服务
    • 范围:用户行为或系统事件触发的通知,如评论回复、文章更新等。
    • 分离思路:保持用户对系统动态的及时了解。
  8. 统计分析
    • 范围:对用户行为和内容表现进行跟踪和分析。
    • 分离思路:为内容创作和系统优化提供数据支持。
8.1 分离的依据与案例
  • 依据:分离的依据通常是基于业务功能的独立性、技术实现的差异性、团队分工的需要以及系统的可维护性和可扩展性。
  • 分离维度: 业务逻辑分离、系统结构分离、处理流程分离、性能分离、安全分离、数据持久化分离、用户界面分离、测试分离、配置和基础设施分离
  • 案例
    • 内容管理ContentService负责处理文章的保存、更新和删除。例如,当用户提交新文章时,ContentService中的saveDraft()方法被调用。
    • 用户管理UserService管理用户账户和权限。例如,grantPermission()方法用于更新用户的角色或访问级别。
    • 审核流程ReviewService执行文章的审核工作。例如,在文章提交后,ReviewServiceconductReview()方法确保内容符合发布标准。
    • 发布控制PublishService控制文章的状态转换。例如,publishArticle()方法将文章状态从草稿更改为已发布。
    • 互动功能InteractionService处理评论和点赞。例如,addComment()方法允许用户在文章下留言。
    • 搜索与导航SearchService提供搜索功能。例如,searchArticles()方法根据用户查询返回相关文章。
    • 通知服务NotificationService发送更新通知。例如,当用户的文章被评论时,sendCommentNotification()方法触发通知发送。
    • 统计分析AnalyticsService跟踪和报告用户行为。例如,trackUserActivity()方法记录用户的阅读习惯和偏好。
9、参考开源框架

许多现代应用程序和框架,如Spring和Hibernate,都采用了关注点分离的原则来设计其架构,以提高代码的模块化和可维护性。

10、总结

关注点分离是软件设计中的一个关键原则,它通过将不同的功能和业务逻辑分离到不同的组件中,帮助我们构建更加清晰、灵活和易于维护的系统。虽然实现关注点分离可能需要更多的设计工作,但它为系统的可持续发展奠定了基础。