软件设计七大原则(上)

前言

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

七大原则

① 开闭原则

② 依赖倒置原则

③ 单一职责原则

④ 接口隔离原则

⑤ 迪米特法则

⑥ 里氏替换原则

⑦ 合成复用原则

开闭原则

开闭原则(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、接口隔离原则侧重约束接口,主要针对抽象需求,针对程序的整体框架的构建

转载请注明出处
//www.greatytc.com/p/4615cfb77e97

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,368评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,941评论 2 374
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,369评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,848评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,719评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,505评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,904评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,528评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,819评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,848评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,652评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,468评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,912评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,095评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,389评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,906评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,120评论 2 339