软件工程六大设计原则总结,案例演示

本文源码:GitHub·点这里 || GitEE·点这里

一、单一职责原则

1、概念描述

对类来说的,即一个类应该只负责一项职责。如果一个类负责两个职责,可能存在职责1变化,引起职责2变化的情况。可以基于抽象逻辑,或者业务逻辑对类进行细化。

2、案例演示

这里基于方法和类的细化都可以,可以根据实际业务选择。

class Animal {
    public void dogVoice (){
        System.out.println("狗叫声:旺旺");
    }
    public void cowVoice (){
        System.out.println("牛叫声:哞哞");
    }
}
class DogVoice {
    public String getDogVoice (){
        return "旺旺" ;
    }
}
class CowVoice {
    public String getCowVoice (){
        return "哞哞" ;
    }
}

3、注意事项

减少代码一处变更引起的程序大规模改动情况,降低类的复杂度,提高类的可读性,可维护性。通常情况下,需要遵守单一职责原则,可以适当违反单一职责原则。

二、接口隔离原则

1、概念描述

客户端不应该依赖它不需要的接口,一个类对另一个类的依赖,应该建立在最小的接口上。

2、案例演示

interface ReadBlog {
    String getBlog () ;
}
interface AdminBlog {
    Boolean insertBlog () ;
    Boolean updateBlog () ;
    Boolean deleteBlog () ;
}
/**
 * 读者只开放博客阅读接口
 */
class Reader implements ReadBlog {
    @Override
    public String getBlog() {
        return null;
    }
}
/**
 * 管理员有博客全部的管理权限
 */
class AdminUser implements AdminBlog,ReadBlog {
    @Override
    public String getBlog() {
        return null;
    }
    @Override
    public Boolean insertBlog() {
        return null;
    }
    @Override
    public Boolean updateBlog() {
        return null;
    }
    @Override
    public Boolean deleteBlog() {
        return null;
    }
}

3、注意事项

接口的设计粒度越小,则应用系统程序越灵活,程序变得灵活也就意味同时结构复杂性提高,开发开发和理解的难度也会变大,可维护性降低。

三、依赖倒转原则

1、概念描述

高层模块不应该依赖低层模块,两者应依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;中心思想是面向接口编程。

2、案例演示

public class C01_FarmFactory {
    public static void main(String[] args) {
        Animal animal = new Dog() ;
        FarmFactory farm = new Farming() ;
        farm.breed(animal) ;
        animal = new Pig() ;
        farm.breed(animal) ;
    }
}
/**
 * 接口声明依赖对象
 */
interface FarmFactory {
    void breed (Animal animal) ;
}
class Farming implements FarmFactory {
    @Override
    public void breed(Animal animal) {
        System.out.println("农场饲养:"+animal.getAnimalName());
    }
}
interface Animal {
    String getAnimalName () ;
}
class Dog implements Animal {
    @Override
    public String getAnimalName() {
        return "牧羊犬";
    }
}
class Pig implements Animal {
    @Override
    public String getAnimalName() {
        return "土猪一号";
    }
}

3、注意事项

相对于系统开发的多变性,抽象的相对稳定。以抽象为基础搭建的架构比以细节为基础的架构要稳定灵活。下层模块尽量都要有抽象类或接口,程序稳定性更好。变量的声明类型尽量是抽象类或接口,这样变量引用和实际对象之间存在一个过渡空间,利于程序扩展和优化。

四、里氏替换原则

1、概念描述

假设如下场景:

  • 存在,一个类型T1,和实例的对象O1
  • 存在,一个类型T2,和实例的对象O2

如果将所有类型为T1的对象O1都替换成类型T2的对象O2,程序的行为不发生改变。那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。

2、案例演示

public class C01_Calculate {
    public static void main(String[] args) {
        BizCalculate bizCalculate = new BizCalculate() ;
        System.out.println(bizCalculate.add(2,3));
    }
}
class Calculate { }
class BaseCalculate extends Calculate {
    public int add (int a,int b){
        return a+b;
    }
}
/**
 * 这里使用组合的方式完成计算
 */
class BizCalculate extends Calculate {
    private BaseCalculate baseCalculate = new BaseCalculate() ;
    public int add (int a,int b){
        return this.baseCalculate.add(a,b);
    }
}

3、注意事项

使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法;子类可以扩展父类的功能,但不能改变原有父类的功能;在适当的情况下,可以通过聚合,组合,依赖等方式解决问题。

五、开闭原则

1、概念描述

开闭原则是编程中最基础、最重要的设计原则,在代码结构的设计设计时,应该考虑对扩展开放,对修改关闭,抽象思维搭建结构,具体实现扩展细节。

2、案例演示

public class C01_BookPrice {
    public static void main(String[] args) {
        ParityBook parityBook = new DiscountBook("Java",100.00) ;
        System.out.println(parityBook.getPrice());
    }
}
interface Book {
    String getName () ;
    Double getPrice () ;
}
/**
 * 平价书籍
 */
class ParityBook implements Book {
    private String name ;
    private Double price ;
    public ParityBook(String name, Double price) {
        this.name = name;
        this.price = price;
    }
    @Override
    public String getName() {
        return this.name ;
    }
    @Override
    public Double getPrice() {
        return this.price ;
    }
}
/**
 * 打折数据扩展价格计算策略
 */
class DiscountBook extends ParityBook {
    public DiscountBook(String name, Double price) {
        super(name, price);
    }
    @Override
    public Double getPrice() {
        double oldPrice = super.getPrice();
        return oldPrice * 0.8 ;
    }
}

3、注意事项

基于开闭原则设计的代码结构可以提高复用性和可维护性,通过接口或抽象类可以约束类的变化行为,基于指定策略对变化行为进行封装,并且能够实现对扩展开放,使用设计模式的基本原则就是遵循开闭原则。

六、迪米特原则

1、概念描述

迪米特原则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外开放任何信息。类与类关系越密切,耦合度越大,耦合的方式很多,依赖,关联,组合,聚合等。

  • 直接朋友概念

两个对象之间有耦合关系,就说这两个对象之间是朋友关系。其中出现成员变量,方法参数,方法返回值中的类称为直接朋友,而出现在局部变量中的类不是直接朋友。从原则上说,陌生的类最好不要以局部变量的形式出现在类的内部。

2、案例演示

public class C01_Employee {
    public static void main(String[] args) {
        HeadCompanyEmpManage empManage = new HeadCompanyEmpManage() ;
        BranchCompanyEmpManage branchEmp = new BranchCompanyEmpManage() ;
        empManage.printEmp(branchEmp);
    }
}
/**
 * 总公司员工
 */
class HeadCompanyEmp {
    public String name ;
    public HeadCompanyEmp(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "HeadCompanyEmp{name='" + name + '}';
    }
}
/**
 * 分公司员工
 */
class BranchCompanyEmp {
    public String name ;
    public BranchCompanyEmp(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "BranchCompanyEmp{name='" + name + '}';
    }
}
/**
 * 分公司员工管理
 */
class BranchCompanyEmpManage {
    // 添加分公司员工
    public List<BranchCompanyEmp> addEmp (){
        List<BranchCompanyEmp> list = new ArrayList<>() ;
        for (int i = 1 ; i <= 3 ; i++){
            list.add(new BranchCompanyEmp("分公司员工"+i)) ;
        }
        return list ;
    }
    // 获取分公司员工
    public void printBranchCompanyEmp (){
        List<BranchCompanyEmp> list = addEmp () ;
        for (BranchCompanyEmp emp:list){
            System.out.println(emp);
        }
    }
}
/**
 * 总公司员工管理,基于迪米特原则,不出现陌生类
 */
class HeadCompanyEmpManage {
    // 添加总公司员工
    public List<HeadCompanyEmp> addHeadEmp (){
        List<HeadCompanyEmp> list = new ArrayList<>() ;
        for (int i = 1 ; i <= 3 ; i++){
            list.add(new HeadCompanyEmp("总公司员工"+i)) ;
        }
        return list ;
    }
    public void printEmp (BranchCompanyEmpManage empManage){
        // 打印分公司员工
        empManage.printBranchCompanyEmp();
        List<HeadCompanyEmp> headEmpList = addHeadEmp () ;
        for (HeadCompanyEmp headCompanyEmp:headEmpList){
            System.out.println(headCompanyEmp);
        }
    }
}

3、注意事项

迪米特原则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此可以降低耦合关系。降低耦合关系,并不是要求完全没有依赖关系,过度的使用迪米特原则,容易产生大量的中间类,导致复杂度变大。所以在使用迪米特原则时要根据实际业务权衡。

七、设计原则总结

设计模式和设计原则的核心思想都是:判断业务应用中可能会变化模块,并且把这些模块独立出来,基于指定的策略进行封装,不要和那些变化的不大的模块耦合在一起,封装思想上基于接口和抽象类,而不是针对具体的实现编程。核心目的就是降低交互对象之间的松耦合度。设计模式和原则都不是可以生搬硬套的公式,个人理解:只要形似,神韵就自然不差。

八、源代码地址

GitHub·地址
http://github.com/cicadasmile/model-arithmetic-parent
GitEE·地址
http://gitee.com/cicadasmile/model-arithmetic-parent

posted @ 2019-12-03 07:37  知了一笑  阅读(...)  评论(... 编辑 收藏