设计模式 - 六大设计原则之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() 。 在这两个抽奖的功能逻辑作为入参后,扩展起来非常方便。

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

在这里插入图片描述

相关文章
|
7月前
|
设计模式 关系型数据库
【设计模式——学习笔记】设计模式简介+七大设计原则介绍(下)
【设计模式——学习笔记】设计模式简介+七大设计原则介绍
22 0
|
7月前
|
设计模式 前端开发 算法
设计模式之设计原则
程序设计的要遵循的一些理论,也可以理解为程序设计的一种要求和目标,是面向对象程序设计的基石,也是面向对象程序设计的质量保障和依据。
39 0
|
8月前
|
设计模式 Java 关系型数据库
Java设计模式中的设计原则 2
Java设计模式中的设计原则
57 0
|
4月前
|
设计模式 关系型数据库 程序员
【设计模式】设计原则
【1月更文挑战第12天】【设计模式】设计原则
|
5月前
|
设计模式 Java
Java设计模式七大原则之依赖倒置原则
Java设计模式七大原则之依赖倒置原则
30 0
|
5月前
|
设计模式 程序员
设计模式-设计原则
设计模式-设计原则
|
7月前
|
设计模式 存储 Java
JAVA设计模式第一讲:设计原则
JAVA设计模式第一讲:设计原则
|
7月前
|
设计模式 Java 程序员
【设计模式——学习笔记】设计模式简介+七大设计原则介绍(上)
【设计模式——学习笔记】设计模式简介+七大设计原则介绍
30 2
|
8月前
|
设计模式 算法 Java
Java设计模式中的设计原则 1
Java设计模式中的设计原则
68 0
|
8月前
|
设计模式 uml
设计模式-浅谈依赖倒置原则
设计模式-浅谈依赖倒置原则