Spring学习01-Spring中的架构设计原则

Spring学习01-Spring中的架构设计原则

1.1 开闭原则(Open-Closed Principle)

开闭原则是指一个软件实体应对扩展开发,对修改关闭;其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。

开闭原则主要是支持每个实体类都可以支持对他的扩充,但不允许直接修改其内容。

Instance

/**
 * @author Xinyuan
 */
public interface IBook {
    Integer getISBN();
    String getName();
    Double getPrice();
}
public class JavaBook implements IBook{

    private Integer ISBN;
    private String Name;
    private Double Price;

    public JavaBook(Integer ISBN, String name, Double price) {
        this.ISBN = ISBN;
        Name = name;
        Price = price;
    }

    @Override
    public Integer getISBN() {
        return ISBN;
    }

    @Override
    public String getName() {
        return Name;
    }

    @Override
    public Double getPrice() {
        return Price;
    }
}

首先我们定义了一个书本的接口,再定义一个JavaBook的实现类,用于网上商城售卖书本。

但此时商场想要做活动,对JavaBook进行打8折的促销,根据开闭原则,我们无法修改原有的代码,因为如果直接修改可能会影响其他地方调用该类的结果。

所以此时我们需要再编写一个JavaDiscountBook的的子类来实现。

public class JavaDiscountBook extends JavaBook{
    public JavaDiscountBook(Integer ISBN, String name, Double price) {
        super(ISBN, name, price);
    }
    @Override
    public Double getPrice() {
        return super.getPrice() * 0.8;
    }
}

OK,此时我们就完成了按照开闭原则进行的新业务的处理

1.2 依赖倒置原则(Dependence Inversion Principle)

依赖倒置原则是指设计代码结构的时候,高层模块不应该依赖底层模块,但二者都可依赖其抽象。抽象不应该依赖细节,细节不应该依赖抽象。

依赖倒置可以减少类与类之间的耦合性。

设计的时候需要先从下层开始思考,将下层模块抽象化,在考虑高层模块

Instance

public class ZhangSan {
    public void BuyJavaBook(){
        System.out.println("张三购买了java书");
    }
    
    public void BuyPythonBook(){
        System.out.println("张三购买了python书");
    }
    
}
    public static void main(String[] args) {
        ZhangSan zhangSan = new ZhangSan();
        zhangSan.BuyJavaBook();
        zhangSan.BuyPythonBook();
    }

但此时如果张三还想要买购买别的书,比如C++的书籍,需要业务扩展,需要从低层到高层一次修改代码,需要再ZhangSan类中增加BuyCppBook() 方法,如此一来系统发布后非常不稳定,也不利于维护。

此时,就需要将所有的书籍定义一个规范,也就接口,所有的书籍都实现它,再通过Java的多态,可以输入不同的实现类

public interface Books {
    void BuyBook();
}

public class JavaBook implements Books{
    @Override
    public void BuyBook() {
        System.out.println("购买了Java书籍");
    }
}
public class PythonBook implements Books{
    @Override
    public void BuyBook() {
        System.out.println("购买了Java书籍");
    }
}
public class ZhangSan {
    public void Buy(Books book){
        System.out.println("张三");
        book.BuyBook();
    }
     public static void main(String[] args) {
        ZhangSan zhangSan = new ZhangSan();
        zhangSan.Buy(new JavaBook());
        zhangSan.Buy(new PythonBook());
    }
}

此时,如果需要拓展业务,只需要创建新的Books的实现类就可以了,这也是接口的好处

延申

实际上,这种方式就是依赖注入,之后会具体展开 还有别的两种注入方式就是通过 构造器(Constructor)和Setter方式

1.3 单一职责原则(Simple Responsibility Principle)

不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。

假设我们有一个类负责两个职责,一旦发生需求的变更,修改其中一个职责的逻辑代码,有可能导致另一个职责的功能发生故障。单一职责原则就是将两个职责用两个不同的类来实现,进行解耦

Instance

用课程举例,课程有直播课和视频课,直播课不能快进,视频课可以反复观看,功能职责不一样。

我们可以这样实现

public class Course {
    public void study(String courseName){
        if ("直播课".equals(courseName)){
            System.out.println("正在学习" + courseName + "不能快进");
        }else if ("视频课".equals(courseName)){
            System.out.println("正在学习" + courseName + "可以反复观看");
        }
    }
}
    public static void main(String[] args) {
        Course course = new Course();
        course.study("直播课");
        course.study("视频课");
    }

但是这样一来,Course类承担了两种处理逻辑。假设现在我们要对课程进行加密,但是直播课和视频课的加密逻辑不一样,就必须要修改代码,而修改代码的逻辑必会相互影响。

此时,单一职责原则就是将两个功能用两个类来实现。

public interface ICourse {
    void study(String courseName);
}

public class LiveCourse implements ICourse{
    @Override
    public void study(String courseName) {
        System.out.println("正在学习" + courseName + "不能快进");
    }
}

public class VideoCourse implements ICourse{
    @Override
    public void study(String courseName) {
        System.out.println("正在学习" + courseName + "可以反复观看");
    }
}
    public static void main(String[] args) {
        ICourse course = new VideoCourse();
        course.study("直播课");
        course = new LiveCourse();
        course.study("视频课");
    }

此时,这两个职责就进行了解耦,后期对不同的职责进行扩展时就可以根据不同职责的特点针对扩展

1.4 接口隔离原则 (Interface Segregation Principle)

接口隔离原则是指使用多个专门的接口,而不是使用单一的总接口,客服端不应该依赖它不需要的接口或方法。

  1. 一个类对另一个类的依赖应建立在最小接口之上
  2. 建立单一接口,不要建立庞大臃肿的接口。
  3. 细化接口,接口中的方法尽量少

接口隔离原则符合我们常说的 高内聚、低耦合的思想,可以使类具有很好的可读性、可扩展性和可维护性。

Instance

现在我们将会对动物的一些行为进行描述

public interface Animal {
    void eat();
    void fly();
    void swim();
}
public class Bird implements Animal{
    @Override
    public void eat() {}

    @Override
    public void fly() {}
    @Override
    public void swim() {}
}

public class Dog implements Animal{
    @Override
    public void eat() {}

    @Override
    public void fly() {}

    @Override
    public void swim() {}
}
/**
可以看出 Bird的 swim()只能空着,Dog的Fly()方法显然使不可能的,这时候就需要我们针对不同动物的行为来设计不同的接口
*/
public interface EatAnimal {
    void eat();
}
public interface FlyAnimal {
    void fly();
}
public interface SwimAnimal {
    void swim();
}
public class Dog implements EatAnimal, SwimAnimal{

    @Override
    public void eat() {}

    @Override
    public void swim(){}
}

定义了三种不同的接口,该动物会有哪些行为就实现哪些接口,这样就非常清晰了

在这里插入图片描述

1.5 迪米特原则(Law of Demeter)

迪米特原则是指一个对象应该对其他对象保持最小的了解。主要强调:之和朋友交流,不和陌生人讲话。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,你(被调用或耦合的类)的内部是如何地复杂与我无关,那是你的事情,我就知道你提供了这么多的public方法,我就调用这么多,其他我一概不关心。

Instance

现在来设计一个权限系统,Boss需要查看目前发布到线上的课程数量,这时候Boss 需要找到TeamLeader来进行统计,TeamLeader再把统计结果告诉Boss。

public class Course {
}

public class TeamLeader {
    public void checkNumberOfCourses(List<Course> courseList){
        System.out.println("目前已发布的课程数量:"+courseList.size());
    }
}

public class Boss {
    public void commandCheckNumber(TeamLeader teamLeader){
        List<Course> courseList = new ArrayList<>();
        //此时模拟Boss在一页一页往下翻名单,TeamLeader做实时统计
        for (int i = 0; i < 20; i++) courseList.add(new Course());
        teamLeader.checkNumberOfCourses(courseList);
    }
}
    public static void main(String[] args) {
        Boss boss = new Boss();
        TeamLeader teamLeader = new TeamLeader();
        boss.commandCheckNumber(teamLeader);
    }

到此,其功能都已经实现了。但是Boss不需要关心具体的Course,也不需要关心TeamLeader到底是如何统计的,Boss只需要最终的一个数字。

修改后

public class TeamLeader {
    public void checkNumberOfCourses(){
        List<Course> courseList = new ArrayList<>();
        //此时模拟Boss在一页一页往下翻名单,TeamLeader做实时统计
        for (int i = 0; i < 20; i++) courseList.add(new Course());
        System.out.println("目前已发布的课程数量:"+courseList.size());
    }
}

public class Boss {
    public void commandCheckNumber(TeamLeader teamLeader){
        teamLeader.checkNumberOfCourses();
    }
}

此时,Boss和Course已经没有关系了。简单的来说,迪米特原则就是尽量将自己不关心的方法放在别的类中做,减少与自己的耦合

1.6 里氏替换原则 (Liskov Substitution Principle)

定义:如果对每一个类型为T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。

约束

(1)子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
(2)子类中可以增加自己特有的方法。
(3)当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
(4)当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

通俗的讲,就是把父类的对象改成子类的对象后,运行结果需要一致。也就是说子类不可以重写父类的方法,如果要进行业务扩展,需要自己增加方法。

Instance

在开闭原则例子中,修改代码后,虽然我们遵循了开闭原则,但还是违背了里氏替换原则。

public class JavaDiscountBook extends JavaBook{
    public JavaDiscountBook(Integer ISBN, String name, Double price) {
        super(ISBN, name, price);
    }
    @Override
    public Double getPrice() {
        return super.getPrice() * 0.8;
    }
}

在JavaDiscountBook中,不能重写父类的方法。如果在业务中,讲父类替换为子类,得到的价格就是原来的0.8倍,这与与拿来的价格不同违背了里氏替换原则。

public class JavaDiscountBook extends JavaBook{
    public JavaDiscountBook(Integer ISBN, String name, Double price) {
        super(ISBN, name, price);
    }
    public Double getDiscountPrice() {
        return super.getPrice() * 0.8;
    }
}

更好的写法就是就是增加新的方法,不修改父类的方法

2.1 Reference

主要材料《Spring 5核心原理与30个类手写实战》

开闭原则:https://blog.csdn.net/sinat_20645961/article/details/48239347

依赖倒置原则:https://www.jianshu.com/p/c3ce6762257c

单一职责原则:https://blog.csdn.net/zhengzhb/article/details/7278174

接口隔离原则:https://blog.csdn.net/king123456man/article/details/81626059

迪米特原则:https://blog.csdn.net/weixin_33984032/article/details/92439242

里氏替换原则:https://blog.csdn.net/zhonghuan1992/article/details/37461407?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值