目录
14.1.1隐式耦合:Cascading Message 级联调用问题
14.5.Summary:Principles from Modularization 模块化的原则
参考:南软考研大书,软工二
这位大佬:
Blog of Samperson (samperson1997.github.io)
还有这位大佬:
SpriCoder的博客
描述的是两个模块之间的关系的复杂程度。
耦合根据其耦合性由高到低分为几个级别:模块耦合性越高,模块的划分越差,越不利于软件的变更和重用。1、2、3不可接受,4、5可以被接受,6最理想
(1)内容耦合(一个模块直接修改另一个模块的内容,如GOTO语句;某些语言机制支持直接更改另一个模块的代码;改变另一个模块的内部数据)
(2)公共耦合(全局变量,模块间共享全局数据,例子:全局变量)
(3)重复耦合(模块间有重复代码)
(4)控制耦合(一个模块给另一个模块传递控制信息,如“显式星期天”)
(5)印记耦合(共享数据结构却只是用了一个部分)
(6)数据耦合(模块间传参只传需要的数据,最理想)
表达的是一个模块内部的联系的紧密性。
内聚性由高到低分为:内聚性越高越好,越低越不易实现变更和重用,3、4、5等价,1、2最好,6、7不能接受。
(1)信息内聚(模块进行许多操作,各自有各自的入口点,每个操作代码相对独立,而且所有操作都在相同的数据结构上进行:如栈)
(2)功能内聚(只执行一个操作或达到一个目的)
(3)通信内聚(对相同数据执行不同的操作:查书名、查书作者、查书出版商)
(4)过程内聚(与步骤有关:守门员传球给后卫、后卫传给中场球员、中场球员传给前锋)
(5)时间内聚(与时间有关:起床、刷牙、洗脸、吃早餐)
(6)逻辑内聚(一系列可替换的操作:如坐车、坐飞机)
(7)偶然内聚(多个不相关的操作:修车、烤面包、遛狗、看电影)
【题型】对实例,说明它们之间的耦合程度与内聚,给出理由。书上P226习题
每个模块都隐藏一个重要的设计决策——职责。职责体现为模块对外的一份契约,并且在这份契约之下隐藏的只有这个模块知道的决策或者说秘密,决策实现的细节仅自己知道。
模块的信息隐藏:模块的秘密(容易变更的地方):根据需求分配的职责、内部实现机制。
【题型】对实例,说明其信息隐藏程度好坏。教材222页
【项目实践】耦合的处理?
分层风格:仅程序调用与简单数据传递
包设计:消除重复
分包:接口最小化
创建者模式:不增加新的耦合
控制者模式:解除View与Logical的直接耦合内聚的处理?
分层:层间职责分配高内聚,层内分包高内聚
信息专家模式
控制器与委托式控制风格信息隐藏处理?
分层与分包:消除职责重复、最小化接口:View独立?数据库连接独立?
模块信息隐藏:模块需求分配与接口定义
类信息隐藏:协作设计,接口定义
变化设计?分层风格、RMI



独立接口避免不必要偶合




这人写得蛮清楚的:深度解析设计模式七大原则之——里氏替换原则
“在派生类中重新定义一种方法时,只能用一个较弱的方法代替其先决条件,而用一个较强的方法代替其后置条件” — B. Meyer,1988年
问题案例
Is a Square a Rectangle?
- Rect r = new Rect();
- setWidth = 4;
- setHeight = 5;
- assert(20 == getArea());
- class Square extends Rect{
- // Square invariant, height = width
- setWidth(x) {
- setHeight()=x;
- }
- setHeight(x) {
- setWidth(x)
- }
- } // violate LSP?
正方形的长宽相同,不必输入width和height两个数。 子类比父类条件更强,多限制条件。说明前置条件过强。
Penguin is a bird?
- class Bird {
- // has beak, wings,...
- public: virtual void fly();
- // Bird can fly
- };
- class Parrot : public Bird {
- // Parrot is a bird
- public: virtual void mimic();
- // Can Repeat words...
- };
- class Penguin : public Bird {
- public: void fly() {
- error ("Penguins don’t fly!");
- }
- };
-
- void PlayWithBird (Bird abird) {
- abird.fly();
- // OK if Parrot.
- // if bird happens to be Penguin...OOOPS!!
- }
企鹅、鸵鸟和几维鸟从生物学的角度来划分,它们属于鸟类;但从类的继承关系来看,由于它们不能继承“鸟”会飞的功能,所以它们不能定义成“鸟”的子类。

课堂练习

左边不好,因为前置条件过强,一般的门不会报警。
右边不好,door不可替代alarm
继承/组合 实例一


案例二

案例三
- class Object {
- public: virtual void update() {};
- virtual void draw() {};
- virtual void collide(Object objects[]) {};
- };
- class Visible : public Object {
- public:
- virtual void draw() {
- /* draw model at position of this object */ };
- private: Model* model;
- };
- class Solid : public Object {
- public:
- virtual void collide(Object objects[]) {
- /* check and react to collisions with objects */ };
- };
- class Movable : public Object {
- public:
- virtual void update() {
- /* update position */ };
- };
改进方案:




“一个类只有一个改变的理由”-罗伯特·马丁(Robert Martin)
问题案例一


案例二

案例三

案例四:

每一个模块都隐藏了这个模块中关于重要设计决策的实现,以至于只有这个模块的每一个组成部分才能知道具体的细节
需要隐藏的两种常见设计决策
面向对象机制
对扩展开放,对修改封闭
违反了OCP原则的典型标志:出现了switch或者if-else
分支让程序增加复杂度,修改时容易产生新错误(特例:创建)
就是那种有扩展的,比如:面向os和面向andriod,这时候需要用工厂模式等把他们分隔开,便于扩展(可能还有面向mac等等)。
(与工厂结合紧密,解决new的创建问题)
I. 高层模块不应依赖底层模块,两者都应依赖抽象
II. 抽象不应依赖细节,细节应依赖抽象
使用抽象类(继承)机制倒置依赖
示例:A依赖于B:B不是抽象类,所以A依赖于具体,而不是抽象,如果需要变更B的行为,就会影响到A
添加抽象类BI,让 B实现(继承)BI:A依赖于BI,B依赖于BI,BI是抽象类,所以是依赖于抽象,BI比较稳定,如果B发生变更,可以通过为BI扩展新的实现(子类型)来满足
题目类似于:

教材263页
需要进行接口和实现的分离:通过接口和实现该接口的类;通过子类继承父类
注意:继承关系(A+B)可能使得灵活性下降,因为父类接口的变化会影响子类,这时可以通过组合关系来解决。
利用抽象类机制实现可修改性和可扩展性:只要方法的接口保持不变,方法的实现代码是比较容易修改的,不会产生连锁反应。通过简单修改创建新类的代码,就可以相当容易地做到扩展新的需求(不用修改大量与类方法调用相关的代码。
利用委托机制实现灵活性:继承的缺陷:一旦一个对象被创建完成,它的类型就无法改变,这使得单纯利用继承机制无法实现灵活性(类型的动态改变)。利用组合(委托)机制可以解决这个问题

减少耦合,符合开闭原则,易于扩展;也符合依赖倒置原则,具体依赖于抽象。
test:通过new参数新建employee,由set函数设置具体策略。
Context:employee,包含了employee的个人属性,set策略,调用接口函数实现策略。
Strategy:包括接口Strategy,以及实现类具体Strategy,Context设置了具体策略后,通过调用接口实现具体策略。

简化一下的模板子:
- /**
- * 策略模式
- * 当一个功能的实现可以使用多种算法或者方式的时候
- * 如果选择在业务代码if等分支语句下硬编码,在类似场景多次出现的时候如果修改会改很多处地方,违反开闭原则
- * 基于开闭,这时会想到将这些'策略'方法进行统一管理,使用的时候直接new这个管理类,调用对应的方法即可
- * 而为了将各个策略方法统一管理(如增加一些日志的打印等操作),抽象一个上下文类context对其进行统一管理
- */
- public class StrategyPattern {
-
- public static void main(String[] args) {
- Context context = new Context();//新建上下文
- Strategy addStrategy = new AddStrategy();//添加具体策略
- context.setStrategy(addStrategy);//设置具体策略
- context.invoke(1, 2);//运算
-
- Strategy minusStrategy = new MinusStrategy();
- context.setStrategy(minusStrategy);
- context.invoke(4, 2);
- }
-
- }
-
- //抽象策略是一个接口
- interface Strategy {
- //里面的策略交给具体策略实现
- void doStrategy(int a, int b);
- }
-
- //具体策略1,实现+
- class AddStrategy implements Strategy {
-
- @Override
- public void doStrategy(int a, int b) {
- System.out.println(a + b);
- }
- }
-
- //具体策略2,实现-
- class MinusStrategy implements Strategy {
-
- @Override
- public void doStrategy(int a, int b) {
- System.out.println(a - b);
- }
- }
-
- //上下文类,管理策略对象以及一些额外的通用逻辑
- class Context {
-
- private Strategy strategy;
-
- //获取具体策略
- public Strategy getStrategy() {
- return strategy;
- }
-
- //设置具体策略
- public void setStrategy(Strategy strategy) {
- this.strategy = strategy;
- }
-
- //根据具体策略,运行
- void invoke(int a, int b) {
- System.out.println("Context invoke start");
- strategy.doStrategy(a, b);
- System.out.println("Context invoke done");
- }
- }
-
职责抽象,接口重用

同样简化一下模板:
- /**数字抽象产品*/
- public interface Digit {
- public void display();
- }
- /**黑色数字类,充当具体产品*/
- public class BlackDigit implements Digit {
- public void display(){
- System.out.println("显示黑色数字");
- }
- }
- /**红色数字,充当具体产品*/
- public class RedDigit implements Digit {
- public void display(){
- System.out.println("显示红色数字");
- }
- }
-
-
- /**字母抽象产品*/
- public interface Letter {
- public void display();
- }
- /**黑色字母,充当具体产品*/
- public class BlackLetter implements Letter {
- public void display(){
- System.out.println("显示黑色字母");
- }
- }
- /**Summer文本框类,充当具体产品*/
- public class RedLetter implements Letter {
- public void display(){
- System.out.println("显示红色子母");
- }
- }
-
-
- /**符号抽象产品*/
- public interface Mark {
- public void display();
- }
- /**黑色符号类,充当具体产品*/
- public class BlackMark implements Mark {
- public void display(){
- System.out.println("显示黑色符号");
- }
- }
- /**红色符号类,充当具体产品*/
- public class RedMark implements Mark {
- public void display(){
- System.out.println("显示红色符号");
- }
- }
-
-
- /**字体颜色抽象工厂*/
- public interface ColourFactory {
- public Digit createDigit();
- public Letter createLetter();
- public Mark createMark();
- }
- /**黑色具体工厂*/
- public class BlackColourFactory implements ColourFactory {
- public Digit createDigit(){
- return new BlackDigit();
- }
- public Letter createLetter(){
- return new BlackLetter();
- }
- public Mark createMark(){
- return new BlackMark();
- }
- }
- /**红色具体工厂*/
- public class RedColourFactory implements ColourFactory {
- public Digit createDigit(){
- return new RedDigit();
- }
- public Letter createLetter(){
- return new RedLetter();
- }
- public Mark createMark(){
- return new RedMark();
- }
- }
-
-
- /**客户端测试类*/
- public class Client{
- public static void main(String args[]){
- //使用抽象层定义
- ColourFactory factory;
- Digit dt;
- Letter lt;
- Mark mk;
- //factory=(SkinFactory)XMLUtil.getBean();
- //为了开闭原则,可以利用反射机制和xml资源获取得到想使用的界面类
- factory = new RedColourFactory();//想更换颜色可以在这里做修改
- dt = factory.createDigit();
- lt = factory.createLetter();
- mk = factory.createMark();
- dt.display();
- lt.display();
- mk.display();
- }
- }
职责抽象,隐藏单件创建的实现。具体工厂的实现只有一个实现。

