聊聊软件设计原则

软件设计知识是一名软件开发人员必须要懂的知识,最近几天今天看了bob大叔的《敏捷软件开发》一书和软件设计相关的一些blog和资料,自己做了一个学习笔记

设计目标

正确性、健壮性、灵活性、可重用性、高效性

降低复杂性

  • 所谓复杂性,就是任何使得软件难于理解和修改的因素。
  • 复杂性的来源主要有两个:代码的含义模糊和互相依赖

    • 模糊指的是,代码里的重要信息看不出来;
    • 依赖指的是,某个模块的代码,不结合其他模块的代码,就无法理解
  • 危害:复杂性的危害在于,它会递增。如果做错了一个决定,导致后面的代码都基于前面的错误实现,只会越来越复杂。"常听人说,我们先把产品做出来,后面再改进",这很难做到

  • ==关键==:找出易变化的部分,合理抽象。
    用抽象构建框架、用实现扩展细节

代码抽象三原则:

1. Don’t Repeat Yourself (DRY)

  • 系统的每一个功能都应该有唯一的实现。也就是说,如果多次遇到同样的问题,就应该抽象出一个共同的解决方法,不要重复开发同样的功能
    不要重复自己

2. You Ain’t Gonna Need It (YAGNI)

  • 定义:你不会需要它,只考虑和设计必须的功能,避免过度设计
  • 这是"极限编程"提倡的原则,指的是你自以为有用的功能,实际上都是用不到的。因此,除了最核心的功能,其他功能一概不要部署,这样可以大大加快开发
  • 你会发现DRY原则和YAGNI原则并非完全兼容。前者追求"抽象化",要求找到通用的解决方法;后者追求"快和省"

3.Rule Of Three

  • 称为"三次原则",指的是当某个功能第三次出现时,才进行"抽象化"。

  • 这样做有几个理由:

    • 省事。如果一种功能只有一到两个地方会用到,就不需要在"抽象化"上面耗费时间了。

    • 容易发现模式。"抽象化"需要找到问题的模式,问题出现的场合越多,就越容易看出模式,从而可以更准确地"抽象化"。

    • 防止过度冗余。如果一种功能同时有多个实现,管理起来非常麻烦,修改的时候需要修改多处。在实际工作中,重复实现最多可以容忍出现一次,再多就无法接受了。

  • 小结:综上所述,"三次原则"是DRY原则和YAGNI原则的折衷,是代码冗余和开发成本的平衡点,值得我们在"抽象化"时遵循

面向对象的S.O.L.I.D原则

1. SRP职责单一原则

  • 核心思想是一个类只做一件事,把事情做好,其只有一个引起它变化的原因,职责过多,引起它变化的原因就越多,将导致责任依赖增加耦合性
  • 遵循单一职责原的优点有:
    • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
    • 提高类的可读性,提高系统的可维护性;
    • 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
  • 应用场景:迭代器

2. 里氏替换原则

  • 定义:所有引用基类的地方必须能透明地使用其子类的对象,替换之后,代码还能正常工作。它是使代码符合开闭原则的重要保证.
  • 问题:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障
  • 解决:当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法
  • 换个说法是,子类可以扩展父类的功能,但不能改变父类原有的功能(不能破坏继承体系)
    1. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
    2. 子类中可以增加自己特有的方法
    3. 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
    4. 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格

3. 接口隔离原则

  • 定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
  • 规约:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少,但不要过度。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  • 示例:人们对电脑有不同的使用方式:如上网、看电影、写文档、玩游戏、通讯、计算和存储等.如果都把这些功能都定义在电脑的抽象类里面。那各种功能类型的电脑(如上网本、服务器、PC、智能学习机)都要现实这些接口。所以,应该把这些接口隔离开,这样不同功能的电脑只需要实现自己需要的接口.
接口隔离2.jpg
接口隔离1.jpg

4. 依赖倒置原则

  • 定义:高层模块不应该依赖底层模块的实现,而是依赖高层抽象。而且,二者都应该依赖于抽象。抽象不应该依赖细节;细节应该依赖抽象
  • 问题:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险
  • 解决:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率
  • 核心思想:面向接口编程,在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
  • 好处:可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。
  • 应用模式:工厂模式
  • 规范:实际编程中,最好做到如下3点:
    1. 低层模块尽量都要有抽象类或接口,或者两者都有。
    2. 变量的声明类型尽量是抽象类或接口。
    3. 使用继承时遵循里氏替换原则

如果依赖的是一个稳定的具体类,那么可以直接依赖它

  • 层次化
    • 方式1:高层模块依赖底层实现模块,这种依赖性是传递的


      依赖倒置1.png
  • 方式2
    - 解除传递依赖关系
    - 接口所有权倒置:客户拥有抽象接口,它的服务者从这些抽象接口中派生,底层模块实现了在高层模块声明并被高层模块调用的接口


    依赖倒置2.png

5. 开放/封闭原则

  • 对扩展开放,对修改关闭。如果有新的需求和变化可以对现有代码进行扩展,以适应新的情况.而不是对原有代码进行修改.
  • 解决:关键在于抽象、预测和刺激变化
  • 应用模式:装饰者模式(如java.io包),不改变原有代码扩展对象的行为

其他原则

1. Keep it sample,stupid(Kiss)

  • 保持简单、直接,不要复杂化
  • 模块分成接口和实现。接口要简单,实现可以复杂。
  • 好的 class 应该是"小接口,大功能",大量的功能隐藏在简单接口之下,对用户不可见,用户感觉不到这是一个复杂的 class.比如Unix 的文件读写接口

2. Program to an interface, not an implementation

  • 面向接口编程,而不是实现
  • 工厂模式、策略模式等等

3. 高内聚低耦合

  • 将模块间的耦合降到最低,努力让一个模块做到精益求精.内聚意味着独立和重用,耦合意味着多米诺骨牌效应.

4. 迪米特法则

又称最少知识原则,该原则告诉我们要降低耦合。

  • 定义:一个对象应该对其他对象保持最少的了解

  • 问题:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

  • 解决:尽量降低类与类之间的耦合

  • 对于对象 ‘O’ 中一个方法’M’,M 应该只能够访问以下对象中的方法:

    • 对象O本身
    • 参数对象
    • 与对象O直接相关的对象
    • 方法中创建或实例化的对象
  • 应用模式:外观模式,提供一个统一的接口来访问子系统中的一群接口。外观定义了一个高层接口,让系统更容易使用

5. 好莱坞原则

  • 你不要找我,我会找你 。 高层组件对待底层组件的方式:别调用我们,我们会调用你
  • 应用场景:
    • 观察者模式,以通知替代轮询
    • 工厂模式
    • Ioc依赖注入,DI控制反转设计的基础,所有组件都是被动的,初始化和调用都由容器负责
    • 模板模式

6. 无环依赖原则

  • 包、服务之间的依赖结构必须是一个直接的无环图形,不能出现循环依赖
  • 打破循环依赖关系,解决关系耦合问题:
    • 使用依赖倒置原则和接口隔离原则
    • 创建新的包,将共同类抽象出来放在新的包里

7. 减少抛异常

  • 除了那些必须告诉用户的错误,其他错误尽量在软件内部处理掉,不要抛出

总结

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

推荐阅读更多精彩内容

  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,734评论 0 14
  • 目录: 设计模式六大原则(1):单一职责原则 设计模式六大原则(2):里氏替换原则 设计模式六大原则(3):依赖倒...
    加油小杜阅读 721评论 0 1
  • 转载自 设计模式六大原则[http://www.uml.org.cn/sjms/201211023.asp#3] ...
    厨子阅读 1,088评论 2 5
  • 坚持分享第400天( 原创总第568天 2018年6月26日 星期二) 小嘟嘟到我家的第一天洗了澡,第二天就...
    红云_杨柳清风阅读 524评论 0 0
  • 【原创诗歌】 只因为年少的轻狂 才渴望去流浪远方 似乎可以再无牵念 也不必在乎谁的微笑 让夕阳的斜晖 模糊离情别绪...
    淡淡青莲阅读 367评论 30 21