【设计模式】七大软件架构设计原则

本专栏将从基础开始,循序渐进,由浅入深讲解常见的设计模式,希望大家都能够从中有所收获,也请大家多多支持。
专栏地址:设计模式实战
所有代码地址:代码地址
如果文章知识点有错误的地方,请指正!大家一起学习,一起进步。

1 七大软件架构设计原则

1.1 开闭原则(修改关闭)

允许对类进行继承扩展,不允许对源代码进行修改。例如水果工厂类中每增加一个水果,都要对水果工厂类进行修改,重新编译。如果水果工厂类FruitFactory只是定义接口生产水果,苹果applefactory、梨子pearfactory等工厂类单独实现fruitfactory接口类,创建不同的水果对象,各种水果的创建互不影响,新增水果,只要新增一种水果类继承fruitfactory,不会对fruitfactory类进行修改,这就是对扩展开发,对修改关闭。

interface FruitFactory{
    
    
    void produceFruit();
}

class AppleFactory implements FruitFactory{
    
    

    public void produceFruit() {
    
    
        System.out.println("生产苹果");
    }
}

class PearFactory implements FruitFactory{
    
    

    public void produceFruit() {
    
    
        System.out.println("生产苹果");
    }
}

1.2 依赖倒置(函数的参数使用抽象)

其核心就是面向接口编程,即当调用的类可能会拓展时用接口代替相应的类。

未使用依赖倒置:

interface BuyGoods{
    
    
    void buy();
}

class BuyQingcai implements BuyGoods{
    
    

    public void buy() {
    
    
        System.out.println("买青菜");
    }
}

class People {
    
    
    public void buy(BuyQingcai buyGoods){
    
    
        buyGoods.buy();
    }
}

public class Test2 {
    
    
    public static void main(String[] args) {
    
    
        People p = new People();
        p.buy(new BuyQingcai());
    }
}

上述看着没什么问题,但是如果他不想买青菜,想买萝卜怎么办?我们当然可以新建一个萝卜类,再给他弄一个buy方法,但是问题是People并没有操作萝卜类的方法,我们还需要在People添加对萝卜类的依赖。这样代码要修改的代码量太多了,模块与模块之间的耦合性太高,只要需要稍微有点变化,就要大面积重构,所以该设计不合理。

使用依赖倒置:

interface BuyGoods{
    
    
    void buy();
}

class BuyQingcai implements BuyGoods{
    
    

    public void buy() {
    
    
        System.out.println("买青菜");
    }
}

class People {
    
    
    public void buy(BuyGoods buyGoods){
    
    
        buyGoods.buy();
    }
}

public class Test2 {
    
    
    public static void main(String[] args) {
    
    
        People p = new People();
        p.buy(new BuyQingcai());
    }
}

1.3 单一职责(只负责一个功能)

一个类或者一个方法只负责一项职责,尽量做到类的只有一个行为原因引起变化。如A类只负责功能A,B类只负责功能B,不要让A类既负责功能A,又负责功能B,这样会导致代码混乱,容易产生bug。

1.4 接口隔离(不实现不需要的接口)

将接口细化**,**类继承接口,类中不需要实现不需要的接口。

1.5 迪米特法则(能封装就封装)

最小关系原则,也就是说如果一个代码块能独立封装在一个类的函数中则最好不要放在调用处。

反例如下:

import java.util.ArrayList;
import java.util.List;

class Employee{
    
    
    private String id;

    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }
}

class SubEmployee {
    
    
    private String id;

    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }
}

class SubEmployeeManager {
    
    
    public List<SubEmployee> setValue(){
    
    
        List<SubEmployee> subEmployees=new ArrayList<SubEmployee>();
        for(int i=0;i<10;i++){
    
    
            SubEmployee subEmployee=new SubEmployee();
            subEmployee.setId("分公司"+i);
            subEmployees.add(subEmployee);
        }
        return subEmployees;
    }
}


class EmployeeManager {
    
    
    public List<Employee> setValue(){
    
    
        List<Employee> employees=new ArrayList<Employee>();
        for(int i=0;i<10;i++){
    
    
            Employee employee=new Employee();
            employee.setId("总公司"+i);
            employees.add(employee);
        }
        return  employees;
    }

    public void printAllEmployee(SubEmployeeManager sub){
    
    
        List<SubEmployee> list1 = sub.setValue();
        for(SubEmployee e:list1){
    
    
            System.out.println(e.getId());
        }

        List<Employee> list2 = this.setValue();
        for(Employee e:list2){
    
    
            System.out.println(e.getId());
        }
    }

}

EmployeeManager的printAllEmployee函数中,此段代码违背了迪米特法则,应该放在SubEmployee中实现:

 List<SubEmployee> list1 = sub.setValue();
for(SubEmployee e:list1){
    
    
	System.out.println(e.getId());
}

使用迪米特原则:

import java.util.ArrayList;
import java.util.List;

class Employee{
    
    
    private String id;

    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }
}

class SubEmployee {
    
    
    private String id;

    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }
}

class SubEmployeeManager {
    
    
    public List<SubEmployee> setValue(){
    
    
        List<SubEmployee> subEmployees=new ArrayList<SubEmployee>();
        for(int i=0;i<10;i++){
    
    
            SubEmployee subEmployee=new SubEmployee();
            subEmployee.setId("分公司"+i);
            subEmployees.add(subEmployee);
        }
        return subEmployees;
    }

    public void printAllSubEmployee(){
    
    
        List<SubEmployee> list1 = setValue();
        for(SubEmployee e:list1){
    
    
            System.out.println(e.getId());
        }
    }
}


class EmployeeManager {
    
    
    public List<Employee> setValue(){
    
    
        List<Employee> employees=new ArrayList<Employee>();
        for(int i=0;i<10;i++){
    
    
            Employee employee=new Employee();
            employee.setId("总公司"+i);
            employees.add(employee);
        }
        return  employees;
    }

    public void printAllEmployee(SubEmployeeManager sub){
    
    
        sub.printAllSubEmployee();

        List<Employee> list2 = this.setValue();
        for(Employee e:list2){
    
    
            System.out.println(e.getId());
        }
    }

}

1.6 里氏替换(不改变父类)

  1. 子类不允许修改父类接口的功能,如果父类对象被子类替换,软件的功能不变。例如父类接口add是加的作用,子类不能将add变成减法。
  2. 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松

1.7 合成复用(成员变量使用抽象)

尽量不使用继承,而是通过过定义接口I,类A实现接口,在需要使用A类的B类中定义接口引用I的对象,通过传入A类,可以在B中调用通过接口I对象调用A的实现方法。通过这种方式,可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。

import java.util.ArrayList;
import java.util.List;

interface Print{
    
    
    void out();
}

class A implements Print{
    
    
    public void out() {
    
    
        System.out.println("AAAAAA");
    }
}

class B {
    
    
    private Print print;

    void setPrint(Print print){
    
    
        this.print = print;
    }

    public void output(){
    
    
        if(print != null)
            print.out();
    }
}

public class Test2{
    
    
    public static void main(String[] args) {
    
    
        B b = new B();
        b.setPrint(new A());
        b.output();
    }
}

小结:

image-20220217190013896

猜你喜欢

转载自blog.csdn.net/Learning_xzj/article/details/125027312