设计模式的设计原则之2.0

  • 七大原则
    • 5、接口隔离(InterfaceSegregation Principle,ISP)
      • 5.1、背景
      • 5.2、定义
      • 5.3、特征
      • 5.4、应用
    • 6、迪米特原则(Law of Demeter,LoD)
      • 6.1、背景
      • 6.2、定义
      • 6.3、特征
      • 6.4、应用
    • 7、合成复用原则(Composite Reuse Principle, CRP)
      • 7.1、背景
      • 7.2、定义
      • 7.3、特征(为什么使用合成/聚合复用,而不使用继承复用?)
      • 7.4、应用
  • 参考

七大原则

设计原则名称 作用
单一职责原则 类的职责要单一,不能将太多的职责放在一个类中
开闭原则 软件实体对扩展是开放的,但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能
里氏代换原则 在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象
依赖倒转原则 要针对抽象层编程,而不要针对具体类编程(抽象不应该依赖细节,细节应该依赖抽象)
接口隔离原则 使用多个专门的接口来取代一个统一的接口
合成复用原则 在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚至不使用继承关系
迪米特法则 一个软件实体对其他实体的引用越少越好,或者说如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互

前四种原则——》》》单一职责、里氏代换、开闭原则、依赖倒转

5、接口隔离(InterfaceSegregation Principle,ISP)

5.1、背景

我们生活中用到的智能手机和智能手表,它们有类似的接口功能,也有不同的功能,比如

  • 手机(phone):看时间、看天气、听音乐、看视频
  • 智能手表(smartwatch):看时间、看天气、测心率

  1. 一个接口中封装了太对的方法,导致phoneSmartwatch这两个类中必须实现一些无用的方法。

  2. 这样的接口稳定性较差,如果phone需要增加一个方法的话,那么`Smartwatch这个实现类中也要相应的实现这个方法(当然方法体内是空的,但是必须要实现的)。

  3. 编码混乱,导致修改时难度增加(需要自己去区分开哪些是这个类中的方法,哪些是另外的一个类中的方法,这样额外增加了工作量)。

应该怎么设计?


将公共的部分抽取出来单独放在一个接口中,自己独有的行为放在相应的接口中,通过独有的这个接口去继承公共的接口,这样的话,就能很好的起到接口的隔离的作用。这个地方我只是举了这样的一个例子,公共的部分是show,那么在实际的使用中,可能是别的相关的功能等。那样的话需要自己去对他们进行抽取。

5.2、定义

客户端不应该依赖它不需要的接口;类间的依赖关系应该建立在最小的接口上。

  • 不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。
  • 接口属于客户,不属于它所在的类层次结构
  • 不要在一个接口里面放很多的方法,这样会显得这个类很臃肿。
    • 接口应该尽量细化,一个接口对应一个功能模块,同时接口里面的方法应该尽可能的少,使接口更加灵活轻便
    • 何为最小的接口,即能够满足项目需求的相似功能作为一个接口,这样设计主要就是为了“高内聚”

注意:接口隔离和单一职责的区分
从功能上来看,接口隔离和单一职责两个原则具有一定的相似性。其实如果我们仔细想想还是有区别的。

(1)从原则约束的侧重点来说,接口隔离原则更关注的是接口依赖程度的隔离,更加关注接口的“高内聚”;而单一职责原则更加注重的是接口职责的划分。

(2)从接口的细化程度来说,单一职责原则对接口的划分更加精细,而接口隔离原则注重的是相同功能的接口的隔离。接口隔离里面的最小接口有时可以是多个单一职责的公共接口。

(3)单一职责原则更加偏向对业务的约束,接口隔离原则更加偏向设计架构的约束。这个应该好理解,职责是根据业务功能来划分的,所以单一原则更加偏向业务;而接口隔离更多是为了“高内聚”,偏向架构的设计。

5.3、特征

接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点:

  • 1、将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  • 2、接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  • 3、如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
  • 4、使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
  • 5、能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

5.4、应用

#include <iostream>using namespace std;class CommonInter {public:CommonInter() {}virtual void showTime() = 0;virtual void showWeather() = 0;
};class phoneInter :public CommonInter
{public:phoneInter() {}virtual void showTime() { cout << "showTime......" << endl;}virtual void showWeather() { cout << "showWeather......." << endl; }void listenMusic(){cout << "listenMusic......" << endl;}void watchVideo(){cout << "watchVideo......" << endl;}
};class watchInter :public CommonInter
{public:watchInter() {}virtual void showTime(){cout << "showTime......" << endl;}virtual void showWeather(){cout << "showWeather......." << endl;}void checkHeartbeat(){cout << "checkHeartbeat......" << endl;}};class phone
{private:phoneInter inter;
public:void use1(){inter.showTime();}void use2(){inter.listenMusic();}
};class watch
{private:watchInter inter;
public:void use1(){inter.showTime();}void use2(){inter.checkHeartbeat();}
};int main(int argc, char *argv[])
{phone p;p.use2();watch w;w.use2();return 0;
}

6、迪米特原则(Law of Demeter,LoD)

也被称为最少知识原则(Least knowledge Principle,LKP)

6.1、背景

只与你的直接朋友交谈,不跟“陌生人”说话。(Talk only to your immediate friends and not to
strangers)

明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则。

在迪米特法则中,对于一个对象,其朋友包括以下几类:

  • (1) 当前对象本身(this);
  • (2) 以参数形式传入到当前对象方法中的对象;
  • (3) 当前对象的成员对象;
  • (4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;
  • (5) 当前对象所创建的对象。

任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。

6.2、定义

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

  • 从依赖者的角度来说,只依赖应该依赖的对象。
  • 从被依赖者的角度说,只暴露应该暴露的方法。
  • 不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达
  • 那么当其中某一个模块发生修改时,就会尽量少地影响其他模块

6.3、特征

迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。

  • 降低了类之间的耦合度,提高了模块的相对独立性。
  • 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

6.4、应用

在运用迪米特法则时要注意以下 6 点。

  • 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
  • 在类的结构设计上,尽量降低类成员的访问权限。
  • 在类的设计上,优先考虑将一个类设置成不变类。
  • 在对其他类的引用上,将引用其他对象的次数降到最低。
  • 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
  • 谨慎使用序列化(Serializable)功能。
    • 举个例子来说,如果你使用 RMI 的方式传递一个对象 VO( Value Object),这个对象就必须使用 Serializable 接口,也就是把你的这个对象进行序列化,然后进行网络传输。突然有一天,客户端的 VO 对象修改了一个 属性的访问权限,从 private 变更为 public 了,如果服务器上没有做出响应的变更的话,就会报序列化失败。 这个应该属于项目管理范畴,一个类或接口客户端变更了,而服务端没有变更,那像话吗?!
#include <iostream>
#include<string>using namespace std;//明星
class Star
{private:string _name;
public:Star(string name):_name(name) {}string getName(){return _name;}
};//粉丝
class Fans
{private:string _name;
public:Fans(string name) :_name(name) {}string getName(){return _name;}
};//媒体公司
class Company
{private:string _name;
public:Company(string name) :_name(name) {}string getName(){return _name;}
};//经纪人
class Agent
{private:Star _myStar;Fans _myFans;Company _myCompany;
public:Agent(Star myStar, Fans myFans, Company myCompany) :_myStar(myStar), _myFans(myFans), _myCompany(myCompany){ }void setStar(Star myStar){_myStar = myStar;}void setFans(Fans myFans){_myFans = myFans;}void setCompany(Company myCompany){_myCompany = myCompany;}void meeting(){cout << _myFans.getName() << "与明星" << _myStar.getName() << "见面了。" << endl;;}void business(){cout << _myCompany.getName() << "与明星" << _myStar.getName() << "洽淡业务。" << endl;}
};int main(int argc, char *argv[])
{Star s ("KOBE");Fans f ("KOBE_FANS");Company c("CBA");Agent agent(s,f,c);agent.meeting();agent.business();Star s1("Lebron James");agent.setStar(s1);agent.business();return 0;
}

7、合成复用原则(Composite Reuse Principle, CRP)

C++类与类之间的存在的几种关系以及UML类图简单说明(依赖、关联、聚合、组合、泛化(继承)、实现)

7.1、背景

汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。下图是用继承关系实现的汽车分类的类图。

从上图 可以看出用继承关系实现会产生很多子类,而且增加新的“动力源”或者增加新的“颜色”都要修改源代码,这违背了开闭原则,显然不可取。

但如果改用组合关系实现就能很好地解决以上问题,其类图如下图所示。

1、聚合关系(Aggregation)

UML:带空心菱形的实线来表示,菱形指向整体

  • 聚合(Aggregation)关系是关联关系的一种,是强关联关系,是整体和部分之间的关系,是 has-a 的关系。

  • 整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。

    • 例如,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。

2、组合关系(Composition)

UML :组合关系用带实心菱形的实线来表示,菱形指向整体

  • 组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的。
  • 在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。
    • 例如,头和嘴的关系,没有了头,嘴也就不存在了。

7.2、定义

合成复用原则又叫组合/聚合复用原则。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

  • 合成/聚合复用原则是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。

7.3、特征(为什么使用合成/聚合复用,而不使用继承复用?)

1、采用组合或聚合复用时
可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能

  • 1、优点

    • 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
    • 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
    • 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
  • 2、缺点:
    • 就是系统中会有较多的对象需要管理。

2、继承复用

继承复用通过扩展一个已有对象的实现来得到新的功能,基类明显的捕获共同的属性和方法,而子类通过增加新的属性和方法来扩展超类的实现。继承是类型的复用。

  • 1、优点

    • (1) 新的实现较为容易,因为超类的大部分功能可以通过继承关系自动进入子类。
    • (2) 修改或扩展继承而来的实现较为容易。
  • 2、缺点
  • (1) 继承复用破坏包装,因为继承将超类的实现细节暴露给了子类。因为超类的内部细节常常对子类是透明的,因此这种复用是透明的复用,又叫“白箱”复用。
  • (2) 如果超类的实现改变了,那么子类的实现也不得不发生改变。因此,当一个基类发生了改变时,这种改变会传导到一级又一级的子类,使得设计师不得不相应的改变这些子类,以适应超类的变化。
  • (3) 从超类继承而来的实现是静态的,不可能在运行时间内发生变化,因此没有足够的灵活性。

7.4、应用

#include<iostream>
#include<vector>
#include<string>
#include<iterator>
using namespace std;
/*
合成复用原则:
继承和组合优先使用组合,避免继承带来的麻烦
人开不同的车,不必人去继承车类,而使用组合,
把车组合进人里面进行调用
*/
class AbstractCar
{public:virtual void run()=0;
};
class BMW : public AbstractCar
{public:virtual void run(){cout << "BMW is run."<<endl;}
};
class Fort : public AbstractCar
{public:virtual void run(){cout << "Fort is run."<<endl;}
};
class People
{public:void setCar(AbstractCar* car){this->car=car;}void Drive(){car->run();delete car;}
private:AbstractCar * car;
};void test()
{People* p=new People();p->setCar(new BMW());p->Drive();p->setCar(new Fort());p->Drive();
}
int main()
{test();return 0;
}

参考

1、https://zhuanlan.zhihu.com/p/24246822
2、http://c.biancheng.net/view/1330.html

面试官问你如何进行程序设计?——设计模式之七大原则——接口隔离、合成复用、迪米特法则以及C++设计实现相关推荐

  1. 【设计模式】软件设计七大原则 ( 接口隔离原则 | 代码示例 )

    文章目录 一.接口隔离原则简介 二.接口隔离原则代码示例 ( 反面示例 ) 1.接口定义 ( 接口臃肿 ) 2.实现类 1 3.实现类 2 三.接口隔离原则代码示例 ( 推荐用法 ) 1.接口 1 2 ...

  2. 面试官问你MyBatis中有哪些设计模式,把这篇文章发给他

    戳蓝字"CSDN云计算"关注我们哦! 作者 | 疯狂的蚂蚁 来源 | https://dwz.cn/KFgol1De 之前总结过一篇Spring中用到了哪些设计模式:<面试官 ...

  3. 设计模式7大原则——接口隔离原则解析(含代码示例)

    一.定义 客户端不应该依赖它不需要的接口,即 一个类对另一个类的依赖应该建立在最小的接口 上 . 通俗的来说就是,不要在一个接口里面放很多的方法,这样会显得这个类很臃肿不堪.接口应该尽量细化,一个接口 ...

  4. python设计模式六大原则_php设计模式的六大原则(六):迪米特法则

    class Teacher { //老师对学生发布命令,清一下女生 public function commond(GroupLeader $groupLeader){ //初始化女生 for($i= ...

  5. php设计模式的六大原则(六):迪米特法则

    为什么80%的码农都做不了架构师?>>>    <?phpclass Teacher {//老师对学生发布命令,清一下女生public function commond(Gro ...

  6. Java设计模式七大原则-接口隔离原则

    接口隔离原则(Interface Segregation Principle) 基本介绍 1) 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上 2) 先看一张图: 类A ...

  7. 设计模式七大原则——接口隔离原则

    1.什么是接口隔离原则? 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口范围上. 2.对应代码 上面这张图呢,就违反了接口隔离原则.它对应的代码如下:

  8. java执行sql文件_面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他

    初识 MyBatis MyBatis 是第一个支持自定义 SQL.存储过程和高级映射的类持久框架.MyBatis 消除了大部分 JDBC 的样板代码.手动设置参数以及检索结果.MyBatis 能够支持 ...

  9. 字节跳动面试官问我看过哪些源码,然后就没有然后了

    最近,我的一位朋友在找工作,已经拿到了美团.快手等公司的Offer,准备选择其中一家入职了. 后来他又接到了字节跳动的电话,通知他去参加三面.从二面到三面之间隔了挺久的,他以为都没戏了,结果就收到了通 ...

最新文章

  1. PL/SQL导入/导出dmp文件-Oracle表空间不一致
  2. LLDB+Python脚本:增强LLDB调试
  3. java8 collect 类型转换_java8新特性之list转换
  4. PHP内核探索:Zend引擎
  5. Linux 下比较文件内容并相同部分、不同部分
  6. 【数据挖掘实例】构建Xgboost模型,在电力用户的95598工单数据中的电费敏感用户预测(高敏用户模型)
  7. Xamarin效果第八篇之视频监控
  8. 拍照比剪刀手泄露指纹信息;国内绿 iPhone11 抢断货;PostgreSQL 12 Beta 4 发布​ | 极客头条...
  9. 《Storm技术内幕与大数据实践》一第1章 绪论
  10. APK的Mokey测试
  11. L1-049 天梯赛座位分配(模拟)
  12. win7自带桌面便签
  13. 四旋翼无人机的动力学模型
  14. 一个非常漂亮的简约大气的table
  15. C++ 实现即时通信软件(直接运行)
  16. 请仔细核对自己的信息
  17. php网页显示左中,php的动态页面在ie内核的浏览器面整体偏左的解决方法静
  18. java 省市联动_省市联动(json)
  19. Java集合移除某个元素
  20. docker 安装linux镜像制作,制作ubuntu完整版docker镜像

热门文章

  1. 从两张Excel表格中筛选相同的值
  2. 基于stm32的四轴无人机和智能车编程实践目录
  3. Python机器学习:一元回归
  4. 电脑显示不了WiFi的解决方法
  5. Mezzanine学习---使用自定义模板
  6. 你学了多久 Python 并能正式工作?
  7. 微信小程序获取页面高度
  8. 【Linux服务器运行jar包】
  9. Outlook2019关联163邮箱
  10. 文墨绘学任何事物都是发展变化的