• JAVA设计模式 —— 软件设计六大原则


    前言:设计模式提供了软件开发过程中一些常见问题的解决方案,一般遵从六大设计原则,分别是开闭原则、里氏替换原则、单一职责原则、最少知道原则(迪米特法则)、依赖倒置原则、接口隔离原则,本文将带大家了解这六大原则

    开闭原则

    一个软件实体如类、对象、函数,应对扩展开放,对修改关闭,这样可以提高系统的可复用性和可维护性。

    实例代码

    public interface IGoods {
        String getName();
        BigDecimal getSalePrice();
    }
    
    • 1
    • 2
    • 3
    • 4

    创建一个商品接口IGoods,包含获取商品名和商品售价的方法

    import java.math.BigDecimal;
    public class Cabbage implements IGoods {
        @Override
        public String getName() {
            return "蔬菜";
        }
        @Override
        public BigDecimal getSalePrice() {
            return new BigDecimal("3.98");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    创建白菜类Cabbage继承接口IGoods,并实现相关方法

    思考一个问题,如果某一天白菜打折了怎么办,也就是说我们如何改动代码来获取新的白菜折扣价?
    这个时候至少有3种可行方式:

    • 直接修改getSalePrice的返回值
    • 在Cabbage类中新增方法getDiscountSalePrice获取打折价
    • 创建类DiscountCabbage实现IGoods接口,在getSalePrice方法中返回折扣价

    但前两种都不是最优方式,第1种方式直接修改了获取打折价方法的实现导致之前用到该方法的地方都发生了变化;第2种方式稍微好一些,但是还是修改了该类的源码;第3种方式是一种符合开闭原则的较好方式,新创建的类不会对代码的其他地方产生影响,只会为后续的某些还需扩展的功能而使用

    import java.math.BigDecimal;
    public class DiscountCabbage implements IGoods {
        @Override
        public String getName() {
            return "蔬菜";
        }
        @Override
        public BigDecimal getSalePrice() {
            return new BigDecimal("1.99");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    创建折扣白菜类DiscountCabbage

    public class TestOCP {
        public static void main(String[] args) {
            IGoods cabbage = new Cabbage();
            System.out.println(cabbage.getName() + "原价:" + cabbage.getSalePrice());
            IGoods discountCabbage = new DiscountCabbage();
            System.out.print(discountCabbage.getName() + "折扣价:" + discountCabbage.getSalePrice());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最后创建一个测试类检查效果即可

    里氏替换原则

    子类可以完全替换父类,也就是说子类继承父类时不应该破坏父类本身的方法,但可以扩展父类的方法

    示例代码

    public class Bird {
        public void fly() {
            System.out.println("鸟正在飞翔...");
        }
        public abstract void prey();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    创建Bird实体类,包含fly和prey两个方法

    public class Eagle extends Bird {
        @Override
        public void prey() {
            System.out.println("鹰正在捕食...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    创建Eagle实体类,继承Bird类并含有prey方法

    public class TestLSP {
        public static void main(String[] args) {
            Bird bird = new Bird();
            bird.fly();
            Bird eagle = new Eagle();
            eagle.fly();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最后创建一个测试类

    单一职责原则

    一个类只能有一个职责,引起该类变化的原因只能有一个

    实例代码

    public class Goods {
        public void action(String type) {
            if (type.equals("进货")) {
                System.out.println("正在进货...");
            } else if (type.equals("出货")) {
                System.out.println("正在出货...");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    以上为一个错误示例类,不符合单一职责原则,原因是该类同时具有进货和出货两种职责功能,在复杂情况下可能会出现某一功能变动影响另一功能的现象,为了符合单一职责原则我们可以进行职责分解

    public class BuyGoods {
        public void action() {
            System.out.println("正在进货...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建实体类BuyGoods只有单一职责——进货

    public class SaleGoods {
        public void action() {
            System.out.println("正在出货...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建实体类SaleGoods只有单一职责——出货

    public class TestSRP {
        public static void main(String[] args) {
            Goods goods = new Goods();
            BuyGoods buyGoods = new BuyGoods();
            SaleGoods saleGoods = new SaleGoods();
            //不符合单一职责原则
            goods.action("进货");
            goods.action("出货");
            //符合单一职责原则
            buyGoods.action();
            saleGoods.action();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    最后创建一个测试类进行实验

    最少知道原则

    又称迪米特法则,一个类不应知道其操作的类的细节,可以理解为只和朋友谈话,不和朋友的朋友谈话。在现实世界中例如老板通过询问销售经理来获取某个商品的销售情况(而不是直接查看某个商品的销售情况)

    实例代码

    public class Cabbage {
        public void getSaleCount() {
            System.out.println("白菜售出100斤");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建商品类Cabbage,添加获取该商品销售量的方法getSaleCount

    public class CabbageManager {
        private Cabbage cabbage;
        public CabbageManager(Cabbage cabbage) {
            this.cabbage = cabbage;
        }
        public void getCabbageSaleCount() {
            cabbage.getSaleCount();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建商品经理类CabbageManager来掌管商品Cabbage

    public class Boss {
        public void getCabbageSaleCount(CabbageManager manager) {
            manager.getCabbageSaleCount();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建老板类boss,通过方法getCabbageSaleCount获知某个商品的销售量

    public class TestLOD {
        public static void main(String[] args) {
            Boss boss = new Boss();
            CabbageManager cabbageManager = new CabbageManager(new Cabbage());
            boss.getCabbageSaleCount(cabbageManager);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    最后编写测试代码即可

    依赖倒置原则

    将抽象层置于高层,实现层依赖于抽象层而抽象层不应依赖于实现层,抽象层应保持稳定,程序的细节变化由实现层来完成

    实例代码

    public interface IGoods {
        public void sale();
    }
    
    • 1
    • 2
    • 3

    创建商品接口IGoods,添加售出方法sale

    public class Cabbage implements IGoods {
        @Override
        public void sale() {
            System.out.println("售出白菜!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    创建类Cabbage实现接口IGoods,实现sale方法

    public class Supermarket {
        //错误示例:商场类和白菜类绑定了
        /*public void saleCabbage() {
            System.out.println("售出白菜!");
        }*/
        //商场和物品类解耦合的实现方式
        public void sale(IGoods iGoods) {
            iGoods.sale();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    创建商场类Supermarket,添加sale方法传入商品接口类型,以将售卖方法抽象化,具体的出售实现由具体的商品类实现即可

    public class TestDIP {
        public static void main(String[] args) {
            Supermarket supermarket = new Supermarket();
            supermarket.sale(new Cabbage());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    创建一个测试类进行简单的测试

    接口隔离原则

    接口包含方法应该适度,在接口被实现时若部分方法由于冗余而被客户端空实现则应将接口拆分

    实例代码

    //错误示例
    public interface IAnimal {
        public void run();
        public void swim();
        public void fly();
    }
    public class Dog implements IAnimal {
        @Override
        public void fly() {}
        @Override
        public void run() {
            System.out.println("狗跑的很快...");
        }
        @Override
        public void swim() {
            System.out.println("狗还会游泳...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果将run、swim和fly方法都放在动物接口IAnimal中,由于Dog类实现时并不需要fly方法导致其为空方法,所以应将该接口拆分

    public interface IRun {
        public void run();
    }
    public interface ISwim {
        public void swim();
    }
    public interface IFly {
        public void fly();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    将三个方法拆分到三个接口中

    public class Dog implements IRun, ISwim {
        @Override
        public void run() {
            System.out.println("狗跑的很快...");
        }
        @Override
        public void swim() {
            System.out.println("狗还会游泳...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    修改Dog类,只需要实现需要的接口即可

    public class TestISP {
        public static void main(String[] args) {
            Dog dog = new Dog();
            dog.run();
            dog.swim();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    简单测试一下代码

    总结
    开闭原则、里氏替换原则、单一职责原则、最少知道原则、依赖倒置原则、接口隔离原则这六大原则贯穿于23种常用设计模式始终,充分的理解和掌握会使得我们对接下来各个设计模式的学习更加畅通

  • 相关阅读:
    摄像头工程师说 Camera - 数据格式 YUV 详解(2)
    安科瑞能耗监测系统在新集卫生院综合楼、急诊楼的设计与应用
    java毕业设计超市网站mybatis+源码+调试部署+系统+数据库+lw
    Apache SeaTunnel本地源码构建编译运行调试
    深x服安服工程师面试
    王道考研计算机网络——传输层
    第 21 章 InnoDB Cluster
    12种数据量纲化处理方式
    svg VS canvas,哪种在移动端适配度更好?实战经历告诉你~
    自然应急项目GNSS监测设备
  • 原文地址:https://blog.csdn.net/Caramel_biscuit/article/details/127720464