美文网首页
软件设计七大原则(上)

软件设计七大原则(上)

作者: Qiansion齐木楠雄 | 来源:发表于2021-05-01 19:52 被阅读0次

前言

仅供学习和参考,文章内容来自相关书籍和搜索引擎以及一些个人的思考和心得。如有纰漏,欢迎指正。

七大原则

① 开闭原则

② 依赖倒置原则

③ 单一职责原则

④ 接口隔离原则

⑤ 迪米特法则

⑥ 里氏替换原则

⑦ 合成复用原则

开闭原则

开闭原则(Open-Closed Principle),对于一个软件实体,应该对拓展开放,对修改关闭。对修改关闭即保证了软件的一个完整性,保留了当前版本的基本功能,在做版本更迭的时候也方便向下兼容。试想如果我们每次更新都直接对代码进行修改,那么需要新功能的用户会进行软件更新,但是那些不需要新功能的呢?他们也必须更新代码,不然就会出问题。所以,在保证了对修改关闭的同时,也必须对拓展开放,拓展性的是每个系统必要条件,因为你永远不知道客户会有什么需求 。
作用
1、通过扩展已有软件系统,可以提供新的行为,以满足对软件新的需求,提高了软件系统的适应性和灵活性
2、已有的软件模块,特别是重要的抽象层模块不能再修改,提高了软件系统的一定的稳定性和延续性
3、这样的设计同时也满足了可复用性和可维护性

在实际开发中,如何去实现开闭原则呢?

实现开闭原则的关键是抽象
1、横向拓展
世界上的高级物种只有人类

public interface Human {
    String getName();
    String getSexLike();
    String getSex();
}

此时,然后女娲首先捏了一个男娃儿

public class man implements Human{
    private String name;
    private String sexLike;
    private String sex;
    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getSexLike() {
        return sexLike;
    }

    @Override
    public String getSex() {
        return sex;
    }
}

然后女娲又捏了一个女娃儿,此时就可以进行横向拓展,而不必修改man中的getSex()的方法让他return "女"。因为man中的getSex()方法可能被其他方法调用。

public class woman implements Human{
    private String name;
    private String sexLike;
    private String sex;
    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getSexLike() {
        return sexLike;
    }

    @Override
    public String getSex() {
        return sex;
    }
}

2、纵向拓展
此时,女娲又捏了一个男娃儿,但是他喜欢男人。此时可以进行纵向拓展

public class gay extends man{
    @Override
    public String getSexLike() {
        return "男";
    }
}

依赖倒置原则

依赖倒置原则(Dependency Inversion Principle)是指高层模块不应该依赖底层模块,两者都应该依赖抽象,抽象不应该依赖细节,细节应该依赖抽象。其中:
高层模块:调用者
底层模块:被调用者
抽象:即是抽象类或者接口,两者是不能够实例化的
细节:即是具体的实现,实现接口或者继承抽象类的类
其实该原则更像是对开闭原则的一种具体实现,如果高层模块直接依赖具体实现,那么每一次修改的时候就必须修改具体实现类(耦合度太高),这就违背了开闭原则。其实依赖倒置原则在我看来更像现在得房屋租赁。大多数房东不能够直接去找到租房人员(高层模块不应该依赖底层模块),大多数租房人员也不容易找到房东,两者都需要通过中介进行交易(两者都应该依赖抽象),中介租房不需要管房子是什么样子的,只需要将它租出去(抽象不应该依赖细节),但租房人员有需求,必须找到中介(细节应该依赖抽象)。
但是我有一点不明白,这个原则为什么叫依赖倒置原则呢?"倒置",不应该是倒过来么?这更像是"中间商原则"。
作用
采用依赖倒置原则可以降低模块之间的耦合性,提高系统的稳定性,减少并行开发的风险,提高代码的可读性和可维护性

在实际开发中,如何去实现依赖倒置原则呢?

在我上述租房例子中,房东就是高层模块,租户就是底层模块,中介就是抽象,房子的类型就是细节
高层模块在开发中就是main()方法,因为它是程序的入口,负责调用其他。

1、不使用依赖倒置原则
底层模块——租户

public class Renter {

    public void rentOneRoom(){
        System.out.println("租户租到1室");
    }
    public void rentOneBadRoom(){
        System.out.println("租户租到1室1厅");
    }
}

高层模块——房东

public class Landlord {
    public static void main(String[] args) {
        Renter renter = new Renter();
        renter.rentOneRoom();
        renter.rentOneBadRoom();
    }
}

租户租到1室
租户租到1室1厅
如果此时租户还想租两室一厅呢?我们又需要去Renter里添加租两室一厅的方法,在landlord里增加两室一厅的调用方法。这样频繁的添加代码,会有一些风险,影响系统的稳定性。

2、使用依赖倒置原则
定义抽象——中介

public interface Agent {
    void rent();
}

定义细节——一室一厅、一室、两室一厅(创建3个java文件,此处图方便,代码贴在一起)

public class OneBadRoom implements Agent{
    @Override
    public void rent() {
        System.out.println("租户租到1室1厅");
    }
}

public class OneRoom implements Agent{
    @Override
    public void rent() {
        System.out.println("租户租到1室");
    }
}

public class TwoBadRoom implements Agent{
    @Override
    public void rent() {
        System.out.println("租户租到2室1厅");
    }
}

定义底层模块——租户

public class Renter {
    public void rent(Agent agent){
        agent.rent();
    }
}

定义高层模块——房东

public class Landlord {
    public static void main(String[] args) {
        Renter renter = new Renter();
        renter.rent(new OneRoom());
        renter.rent(new OneBadRoom());
        renter.rent(new TwoBadRoom());
    }
}

租户租到1室
租户租到1室1厅
租户租到2室1厅
这样以后租户想租新的房子只需要增加细节(房子的类型)即可。而不需要修改底层代码。这种方式就叫做依赖注入。
其他依赖注入方式还有
构造器方式
修改租户Renter

public class Renter {
    private Agent agent;
    public Renter(Agent agent){
        this.agent = agent;
    }
    public void rent(){
        agent.rent();
    }
}

修改房东Loadlord

public class Landlord {
    public static void main(String[] args) {
        Renter renter = new Renter(new OneRoom());
        renter.rent();
    }
}

租户租到1室
setter方式
修改租户Renter

public class Renter {
    private Agent agent;

    public void setAgent(Agent agent){
        this.agent = agent;
    }
    public void rent(){
        agent.rent();
    }
}

修改房东Loadlord

public class Landlord {
    public static void main(String[] args) {
        Renter renter = new Renter();
        renter.setAgent(new OneRoom());
        renter.rent();
    }
}

租户租到1室

单一职责原则

单一职责原则(Simple Responsibility Principle),顾名思义,就是专人干专事。反过来想,如果系统出了问题,有多种原因要去改一个类,那就说明这个类有多种职责。而且单一职责并不是单一功能。
作用
将功能分类,模块划分明确,修改一个模块不会造成其他模块的修改,降低模块之间的耦合度
比如查找一个商品信息,前台查找商品信息用于展示,后台查询商品信息用于管理。但是如下代码既承担了前台查询的职责,又承担了后台查询的职责。那么前台需要展示的信息需要改变时,会对后台也产生影响
1、不遵循单一职责原则

@Repository
public class Repository {
    @Select("<script>" +
            "select feild1,field2,field3 from table")
    List<XXX> getSomeThing();
}

2、遵循单一职责原则

@Repository
public class Repository {
    @Select("<script>" +
            "select feild1,field2,field3 from table")
    List<XXX> getSomeThingForProtal();

    @Select("<script>" +
            "select feild1,field4,field5 from table")
    List<XXX> getSomeThingForBackend();
}

创建两个方法分别服务前台后台,这就满足了粗粒度的单一职责原则。
我们常说的高内聚低耦合原则其实就是单一职责的具体体现。单一职责原则是最简单也非常难实现的原则

接口隔离原则

接口隔离原则(interface Segregation Principle)是指用多个专门的接口,而不使用单一的总接口,客户端不应该只依赖它不需要的接口。这个原则是在接口设计时的规范:
1、一个类对一类的依赖应该建立在最小的接口之上。
2、建立单一接口,不要建立庞大臃肿的接口。
3、尽量细化接口,接口中的方法尽量少(不是越少越好,一定要适度)。
作用
防止庞大,臃肿的接口,避免接口污染,提高程序设计要求的细致划分性。降低大面积维护成本,一旦出现接口污染,会造成实现类中存在大量的不相关不需要去重写的方法

接口隔离原则符合我们常说的高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性。我们在设计接口的时候,要多花时间去思考,要考虑业务模型,包括以后有可能发生变更的地方还要做一些预判。所以,对于抽象,对业务模型的理解是非常重要的。
1、不遵循接口隔离原则
定义一个Animal类

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()方法显然不可能的。
2、遵循接口隔离原则
这时候,我们针对不同动物行为来设计不同的接口,分别设计 IEatAnimal,IFlyAnimal 和ISwimAnimal 接口

public interface EatAnimal {
    void eat();
}
public interface FlyAnimal {
    void fly();
}
public interface SwimAnimal {
    void swim();
}

Dog 只实现 IEatAnimal 和 ISwimAnimal 接口

public class Dog implements EatAnimal, SwimAnimal{
    @Override
    public void eat() {}
    @Override
    public void swim() {}
}

这样拆分下来,就清晰很多。接口隔离原则也是高内聚低耦合的思想,那么它跟接口单一职责有什么区别呢?
单一职责和接口隔离辨析
1、单一职责原则侧重职责,接口隔离侧重对接口的依赖的隔离
2、单一职责原则侧重约束类,其次是接口,针对程序中实现的细节
3、接口隔离原则侧重约束接口,主要针对抽象需求,针对程序的整体框架的构建

转载请注明出处
https://www.jianshu.com/p/4615cfb77e97

相关文章

  • 面向对象软件设计七大原则

    介绍 软件设计的七大原则如下: 开闭原则 依赖倒置原则 单一职责原则 接口隔离原则 迪米特法则 里式替换原则 合成...

  • 七大软件设计原则

    七大软件设计原则 一、开闭原则(Open-Closed Principle) 定义:一个软件实体如类、模块和函...

  • 设计模式03-结构型模式

    写在前面 软件设计七大原则开闭原则里氏替换原则依赖倒置原则单一职责原则接口隔离原则迪米特法则合成复用原则 创建型模...

  • 设计模式04-行为型模式

    写在前面 软件设计七大原则开闭原则里氏替换原则依赖倒置原则单一职责原则接口隔离原则迪米特法则合成复用原则 创建型模...

  • 设计模式01-软件设计七大原则

    写在前面 软件设计七大原则开闭原则里氏替换原则依赖倒置原则单一职责原则接口隔离原则迪米特法则合成复用原则 创建型模...

  • 设计模式02-创建型模式

    写在前面 软件设计七大原则开闭原则里氏替换原则依赖倒置原则单一职责原则接口隔离原则迪米特法则合成复用原则 创建型模...

  • 软件设计七大原则(上)

    前言 仅供学习和参考,文章内容来自相关书籍和搜索引擎以及一些个人的思考和心得。如有纰漏,欢迎指正。 七大原则 ① ...

  • 软件设计七大原则

    软件设计七大原则 1.开闭原则定义:一个软件实体如类、模块和函数应该对扩展开发,对修改关闭用抽象构建框架,用实现扩...

  • 七大软件设计原则

    开闭原则 开闭原则(Open-Closed Principle, OCP)是指一个软件实体如类、模块和函数应该对扩...

  • 七大软件设计原则

    软件设计原则是比较抽象的概念,在设计模式中经常体现初设计原则的概念,但是设计模式中也会有取舍,可能部分体现了,也可...

网友评论

      本文标题:软件设计七大原则(上)

      本文链接:https://www.haomeiwen.com/subject/simarltx.html