初识设计模式、软件设计的六大原则

总结:本篇文字分为两个部分。第一部分:设计模式基本常识;第二部分:软件设计中的六大原则,并详细分析了单一职责原则。(本篇文章的时间轴参考:为知笔记支撑文件夹\Java设计模式(时间序列图).vsdx)

部分一:初识设计模式

什么是设计模式?James拿到这个论点时,很是迷惑!

模式?是不是一个模子?模式识别——计算机领域的经典问题?

设计模拟?软件的设计模式?不懂!!!

但是在实际编码、调试过程中,James的遇到过很是难解的问题:工程代码中有过多的冗余代码——代码复用性不高;需求一旦改变,需要更改很多地方的代码逻辑——代码灵活性不强……

那先看看设计模式的概念吧!

设计模式是一套被反复使用、为多数人知晓、经过分类编目的、代码设计经验的总结。为了编写可重用性代码,让代码更容易被他人理解,并保证代码可靠性而使用的设计思想。

设计模式使代码编制真正工程化。

设计模式是软件行业智慧积累的结晶;它提出了一系列标准术语,概括了相关行业中经验丰富的从业者所应用的所有概念和方法。

常用的23种设计模式如下:

1.单例模式;

2.工厂方法模式; 

3.抽象工厂模式; 

4.模版方法模式; 

5.建造者模式; 

6.代理模式; 

7.原型模式; 

8.中介者模式; 

9.命令模式; 

10.责任链模式; 

11.装饰模式; 

12.策略模式; 

13.适配器模式; 

14.迭代器模式; 

15.组合模式; 

16.观察者模式; 

17.门面模式; 

18.备忘录模式;

19.访问者模式; 

20.状态模式; 

21.解释器模式; 

22.享元模式; 

23.桥梁模式;

设计模式的起源是面向对象程序设计思想,是面向对象设计的精髓——抽象;面向对象通过类和对象来实现抽象,实现时产生了面向对象的三种重要机制:封装、继承和多态而这三种机制衍生了各式各样的设计模式。

在运用面向对象思想进行软件设计时,需要遵循以下原则:

1. 单一责任原则;

2. 里氏替换原则;

3. 依赖倒置原则;

4. 接口隔离原则;

5. 迪米特法原则;

6. 开闭原则;

这23种设计模式按设计意图可组织成五类:接口型模式,责任型模式、构造型模式,操作型模式以及扩展型模式模式的设计意图指出了应用一个模式的价值所在。但上面所说的23中的某种设计模式,并不是仅仅支持一种设计意图。

在软件设计过程中,只要我们尽量遵循以上设计原则,设计出来的软件一定是优秀的,且足够健壮、稳定,并有足够的灵活性来迎接需求的变更。

那James还是不知道为什么,比如:这些原则的含义是什么?它们为什么而出现,解决了什么工程问题?这些原则和之前讲述的23种设计模式有什么联系?如何在我们的工程代码中使用……

这些疑问都需要James一一解决,不过James相信自己肯定能够战胜这些困难。(纵使是住着握手楼、吃着方便面、挤着公交车,只要能够活下来,就有希望!别人能做出来的东西,自己为什么不行?要有必胜的信心!)

部分二:从设计原则开始——单一职责原则

单一职责原则:应该有且仅有一个原因引起类的变更。

为什么在这里出现的是类?是不是仅仅只应用于类的设计?

(为什么会出现这种设计原则?)当一个类承担了过多的职责,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者一直这个类完成其他职责的能力。问题就出在耦合性上!

软件设计真正要做的工作是:发现职责并把那些职责互相分离。单一职责可使类的复杂性降低,其实现的工作有明确且清晰的定义。

那问题来了,James不明白什么是“类的变更”?

(如何实践单一职责原则?)考虑到如何使用代码实现单一责任原则(代码即是思路嘛),好在有百度和谷歌。下面就使用网站上的例子讲解,James认为:用这个例子比较容易说明问题。

需求分析:烹饪,做一道鱼香茄子。

public interface Cooking {
    // 获取菜名
    String getCookingName();
    void setCookingName(String cookingName);
    
    // 烹饪之前的准备工作:摘菜、洗菜等等,准备相应的食材
    void doPrepare(String vegetableName);
    
    // 烹饪模式:蒸、煮、炒、炸等,并提供菜名
    void doFire(String cookingName, String mode);
}

如果James想要烹饪一道鱼香茄子,可以覆写以上四个方法,从获知菜名、准备食材到具体的烹饪操作。从信息和行为的角度来说,前面两个方法是鱼香茄子相关的信息,后两个则是相关的行为(烹饪具体的行为)。

如果考虑如下方式是否更完善?

public interface ICookingInfo {
    // 获取菜名
    String getCookingName();
    void setCookingName(String cookingName);
}
public interface ICookingFire {
    // 烹饪之前的准备工作:摘菜、洗菜等等,准备相应的食材
    void doPrepare(ICookingInfo info);

    // 烹饪模式:蒸、煮、炒、炸等,并提供菜名
    void doFire(ICookingInfo info, String mode);
}

Cooking类包含两个部分,一个部分的职责是获取菜名及相关信息(口味、特点…),另一个部分的职责是具体烹饪(当然需要菜名相关信息啦)

如果ICookingInfo发生改变,势必会造成ICookingFire的改变。对于职责不同的两个部分,需要拆分。那在Cooking类中,只需要实现上述两个不同接口,即可实现烹饪一道菜的功能。

public class Cooking implements ICookingInfo, ICookingFire {
    @Override
    public String getCookingName() {
        return null;
    }

    @Override
    public void setCookingName(String cookingName) {

    }

    @Override
    public void doPrepare(ICookingInfo info) {

    }

    @Override
    public void doFire(ICookingInfo info, String mode) {

    }

}

产生了Cooking对象后,可将其视为ICookingInfo或者ICookingFire接口使用;如果需要设置菜名或其他信息,可以当做是ICookingInfo的实现类;要是进行烹饪操作,就当做是ICookingFire的实现类。(突然引出了一个问题:James貌似不太理解类和其实现的接口之间的关系……)

总之,在接口设计中,如果接口可细分为不同的“职责”,就可将该接口进一步细分。

上述仅仅只是从接口角度解读了:单一职责原则,此外还可以用在类的设计、方法的定义上。归结为一点:自己的事情自己做,并承担相应的责任。

可参考一下实例分析:

1. http://blog.csdn.net/zhengzhb/article/details/7278174 从类的角度解读;

2. 待续……

单一职责原则的优点:

1. 类的复杂性降低,实现什么职责都有清晰明确的定义;

2. 可读性提高;

3. 可维护性提高;

4. 变更引起的风险降低;如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,对系统的扩展性、维护性都有非常大的帮助

单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。

单一职责原则可以使用到哪些方面?单一职责原则适用于接口、类,同样适用于方法(一个方法尽可能只做一件事情)。接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。

但凡事都有需要权衡利弊的地方,不能因为生搬硬套单一职责原则,而让系统变得繁杂而庞大。

posted @ 2016-06-18 22:07  jamesK4W  阅读(3240)  评论(2编辑  收藏  举报