设计模式 - 六大设计原则之DIP(依赖倒置原则)

简介: 设计代码架构时,高层模块不应该依赖于底层模块,二者都应该依赖于抽象。 抽象不应该依赖于细节,细节应该依赖于抽象。 依赖倒置原则是实现开闭原则的重要途径之一, 它降低了类之间的耦合,提高了系统的稳定性和可维护性。

@[toc]

在这里插入图片描述


概述

设计代码架构时,高层模块不应该依赖于底层模块,二者都应该依赖于抽象。 抽象不应该依赖于细节,细节应该依赖于抽象。

依赖倒置原则是实现开闭原则的重要途径之一, 它降低了类之间的耦合,提高了系统的稳定性和可维护性。


Case

抽奖系统服务


Bad Impl

先用最直接的方式,即按照不同的抽奖逻辑定义出不同的接口,让外部服务调用

【抽奖用户类】

public class BetUser {

    private String userName;  // 用户姓名
    private int userWeight;   // 用户权重

    public BetUser() {
    }

    public BetUser(String userName, int userWeight) {
        this.userName = userName;
        this.userWeight = userWeight;
    }

   // set get 
}

普通对象,包括了姓名和权重 。

接下来实现两种不同的抽奖逻辑,在一个类中用两个接口实现

public class DrawControl {

    // 随机抽取指定数量的用户,作为中奖用户
    public List<BetUser> doDrawRandom(List<BetUser> list, int count) {
        // 集合数量很小直接返回
        if (list.size() <= count) return list;
        // 乱序集合
        Collections.shuffle(list);
        // 取出指定数量的中奖用户
        List<BetUser> prizeList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            prizeList.add(list.get(i));
        }
        return prizeList;
    }

    // 权重排名获取指定数量的用户,作为中奖用户
    public List<BetUser> doDrawWeight(List<BetUser> list, int count) {
        // 按照权重排序
        list.sort((o1, o2) -> {
            int e = o2.getUserWeight() - o1.getUserWeight();
            if (0 == e) return 0;
            return e > 0 ? 1 : -1;
        });
        // 取出指定数量的中奖用户
        List<BetUser> prizeList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            prizeList.add(list.get(i));
        }
        return prizeList;
    }

}

包含了两个接口,一个随机抽奖,另外一个按照权重排序

【单元测试】

 @Test
    public void test_DrawControl(){
        List<BetUser> betUserList = new ArrayList<>();
        betUserList.add(new BetUser("花花", 65));
        betUserList.add(new BetUser("豆豆", 43));
        betUserList.add(new BetUser("小白", 72));
        betUserList.add(new BetUser("笨笨", 89));
        betUserList.add(new BetUser("丑蛋", 10));

        DrawControl drawControl = new DrawControl();
        List<BetUser> prizeRandomUserList = drawControl.doDrawRandom(betUserList, 3);
        logger.info("随机抽奖,中奖用户名单:{}", JSON.toJSON(prizeRandomUserList));

        List<BetUser> prizeWeightUserList = drawControl.doDrawWeight(betUserList, 3);
        logger.info("权重抽奖,中奖用户名单:{}", JSON.toJSON(prizeWeightUserList));
    }

在这里插入图片描述

从测试结果上没啥问题。

但如果考虑扩展性的话,就不太友好了。 每次扩展都需要新增接口,同时对于调用方来说需要新增调用的接口代码。

其次,说着接口数量的增加,代码行数就会爆炸,难以维护。

在这里插入图片描述


Better Impl

为了良好的扩展性,可以采用依赖倒置、面向抽象编程的方式实现

在这里插入图片描述

首先定义抽奖接口,任何一个实现方都可实现自己的抽奖逻辑。

【抽奖接口】

public interface IDraw {

    // 获取中奖用户接口
    List<BetUser> prize(List<BetUser> list, int count);

}

【随机抽象实现】

public class DrawRandom implements IDraw {

    @Override
    public List<BetUser> prize(List<BetUser> list, int count) {
        // 集合数量很小直接返回
        if (list.size() <= count) return list;
        // 乱序集合
        Collections.shuffle(list);
        // 取出指定数量的中奖用户
        List<BetUser> prizeList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            prizeList.add(list.get(i));
        }
        return prizeList;
    }

}

【权重抽奖实现】

public class DrawWeightRank implements IDraw {

    @Override
    public List<BetUser> prize(List<BetUser> list, int count) {
        // 按照权重排序
        list.sort((o1, o2) -> {
            int e = o2.getUserWeight() - o1.getUserWeight();
            if (0 == e) return 0;
            return e > 0 ? 1 : -1;
        });
        // 取出指定数量的中奖用户
        List<BetUser> prizeList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            prizeList.add(list.get(i));
        }
        return prizeList;
    }

}

---------
【创建抽奖服务】

public class DrawControl {

    private IDraw draw;

    public List<BetUser> doDraw(IDraw draw, List<BetUser> betUserList, int count) {
        return draw.prize(betUserList, count);
    }

}

这个类中体现了依赖倒置的重要性,可以把任何一种抽奖逻辑传递给这个类。 这样的好处是可以不断的扩展,但是不需要在外部新增调用接口,降低了一套代码的维护成本,提高了可扩展性和可维护性。

这里的重点是把实现逻辑的接口作为参数参数

【单元测试】

   @Test
    public void test_DrawControl() {

        List<BetUser> betUserList = new ArrayList<>();
        betUserList.add(new BetUser("花花", 65));
        betUserList.add(new BetUser("豆豆", 43));
        betUserList.add(new BetUser("小白", 72));
        betUserList.add(new BetUser("笨笨", 89));
        betUserList.add(new BetUser("丑蛋", 10));

        DrawControl drawControl = new DrawControl();
        List<BetUser> prizeRandomUserList = drawControl.doDraw(new DrawRandom(), betUserList, 3);
        logger.info("随机抽奖,中奖用户名单:{}", JSON.toJSON(prizeRandomUserList));

        List<BetUser> prizeWeightUserList = drawControl.doDraw(new DrawWeightRank(), betUserList, 3);
        logger.info("权重抽奖,中奖用户名单:{}", JSON.toJSON(prizeWeightUserList));
    }

可以看到,入参新增了 new DrawRandom()new DrawWeightRank() 。 在这两个抽奖的功能逻辑作为入参后,扩展起来非常方便。

以这种抽象接口为基准搭建起来的框架会更加的稳定,算程已经建设好,外部只需要实现自己的算子即可,最终把算子交给算程处理。

在这里插入图片描述

相关文章
|
6月前
|
设计模式
设计模式六大原则之依赖倒置原则
设计模式六大原则之依赖倒置原则
|
3月前
|
设计模式 Java 测试技术
Java设计模式-UML与设计原则(1)
Java设计模式-UML与设计原则(1)
|
4月前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
|
5月前
|
设计模式 算法
交易链路设计原则&模式问题之中介者(Mediator)方法设计模式是什么,如何解决
交易链路设计原则&模式问题之中介者(Mediator)方法设计模式是什么,如何解决
|
7月前
|
设计模式 算法 Java
【设计模式系列笔记】设计模式与设计原则
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 设计原则是一些通用的设计指导方针,它们提供了如何设计一个优秀的软件系统的基本思想和规则。指导着设计者如何组织代码以实现高内聚、低耦合、易扩展和易维护的软件系统。
92 4
|
7月前
|
设计模式 Java 数据安全/隐私保护
设计模式之六大设计原则
设计模式之六大设计原则
78 0
|
7月前
|
设计模式 关系型数据库 程序员
【设计模式】设计原则
【1月更文挑战第12天】【设计模式】设计原则
|
7月前
|
设计模式 Java
Java设计模式七大原则之依赖倒置原则
Java设计模式七大原则之依赖倒置原则
76 0
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。