【设计模式】设计原则--面向接口编程你理解的对吗?

最近看了《Head First Design Patterns》这本书。正如其名,这本书讲的是设计模式(Design Patterns),而这本书的第一章,讲的是很重要的一些设计原则(Design Principles)。

  • Identify the aspects of your application that vary and separate them from what stays the same.(识别应用程序中各个方面的变化,并将它们与保持不变的部分分开。)

  • Program to an interface, not an implementation.(面向接口而不是实现编程。)

  • Favor composition over inheritance.(优先考虑组成而不是继承。)

其中令我感触颇深的是,“面向接口而不是实现编程”颠覆了我一直以来的认识。

文章中示例代码为原书中截图,C#代码参照文末提供链接。

开始

书中用了一个很形象的示例:模拟鸭子程序(SimUDuck)。系统的最初设计使用标准的OO技术,并创建了一个Duck基类,所有其他Duck类型都继承自该基类。

设计系统时考虑到鸭子都会发出叫声,而且都会游泳,于是将Quack方法和Swim方法定义到Duck基类中并实现;此外,并不是所有的鸭子都是长得一样的,那么将Display方法在Duck基类中定义为抽象的,所有继承自Duck基类的子类编写自己的实现。

新的需求产生了!我们需要让系统中的鸭子可以飞。从面向对象的角度来考虑,如果我们想要代码重用,只需要在Duck基类中添加方法Fly并实现它——所有的鸭子子类都是继承自Duck基类的——就实现了让鸭子飞的功能。我们通过继承实现了代码重用,很轻松就解决了问题。

也许我们需要深入考虑一下,所有的鸭子都会飞吗?玩具橡胶鸭呢?我们把Fly方法的定义及实现放到了Duck基类中,所有继承自它的子类都继承到了Fly方法,其中也包括了不应继承Fly方法的子类。如果按照上面的方案,那我们只能在RubberDuck橡胶鸭子类中重写父类的Fly方法让RubberDuck执行Fly的时候什么都不做。

再深入一些,如果我们的系统中除了橡胶鸭外,还有其他各种鸭子,比如木头鸭子呢?这时DecoyDuck木头鸭子继承来的Quack方法出现了问题——木头鸭子不会叫!我们只好再把DecoyDuck中的Quack方法重写了......

如果我们改用接口会怎么样呢?把QuackFly方法从基类中拿出来,分别在IQuackableIFlyable接口中定义,然后我们不同的子类根据需要来继承接口,并实现QuackFly方法。

当我们有很多个子类鸭子的时候,就要分别为每个继承了IQuackableIFlyable接口的子类来编写QuackFly的实现方法,这完全破坏了代码重用!值得注意的是,虽然我们在这里使用了接口,但这并不是面向接口编程。

封装变化

这里引入第一条设计原则:Identify the aspects of your application that vary and separate them from what stays the same.(识别应用程序中各个方面的变化,并将它们与保持不变的部分分开。)

换言之:take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that don’t.(将变化的部分封装起来,以便以后可以更改或扩展变化的部分而不会影响那些不变的部分。)

这样带来的好处是,我们可以进行更少的代码更改来实现需求功能,减少因代码更改而带来的意想不到的影响,并且提高了系统灵活性。

我们知道Duck的不同子类中,QuackFly的行为是会发生变化的,那么我们将QuackFly方法从Duck基类中拿出来,并为QuackFly方法分别创建一些类,来实现各种不同的行为。

面向接口而不是实现编程

设计原则:Program to an interface, not an implementation.(面向接口而不是实现编程。)

在这里,面向接口而不是实现编程,和封装变化是相辅相成的。值得注意的是,这里所说的接口,并不是我们代码层面上的interface,"面向接口编程(Program to an interface)所表达的意思实际上是面向基类编程(Program to a supertype),核心思想是利用面向对象编程的多态性。在代码的具体实现上,我们既可以用Interface来作为我们所面向的接口,也可以用一个抽象的基类来作为我们面向的接口。遵循面向接口编程,对模拟鸭子程序的FlyQuack行为进行设计,我们可以定义接口IFlyBehaviorIQuackBehavior来代表行为FlyQuack,接口的实现则是行为具体的表现形式。我们可以将接口的不同实现类,来赋值给Duck的不同子类,从而利用继承多态来实现面向接口编程。类图如下:

FlyBehavior是一个所有不同的Fly类都要继承的接口或基类,其中定义了Fly方法。不同的Fly类有不同的Fly方法实现。QuackBehavior类似。

接下来我们对Duck类进行更改,将FlyQuack委托出去,不再通过Duck类或其子类的方法来实现。

  1. 首先我们在Duck类中定义两个代表FlyBehaviorQuackBehavior的变量。这两个变量的值是不同的Duck所需要的特定FlyBehaviorQuackBehavior的子类:

  2. 然后实现PerformQuack方法:

  3. FlyBehaviorQuackBehavior赋值:

至此我们就实现了面向接口编程。

我们还可以动态设置Duck的行为,只需要为Duck类的FlyBehaviorQuackBehavior提供Set方法(在C#中,使用自动属性即可)。

优先考虑组成而不是继承

Favor composition over inheritance.(优先考虑组成而不是继承。)

HAS-A(有一个)比IS-A(是一个)要好。HAS-A在我们的Duck系统中可以描述为:每一个DuckHAS-A有一个FlyBehavior,还HAS-A有一个QuackBehaviorDuck委托它们来处理FlyQuack的行为。优先考虑组合而不是继承让我们的系统拥有更多的灵活性,封装变化,还可以在运行时动态更改类的行为。

(0)

相关推荐

  • 《软件设计的哲学》解读

    引言 本书的英文名称为<A Philosophy of Software Design>,由斯坦福大学教授.Tcl 语言发明者 John Ousterhout所著,在bookstack上有 ...

  • 为什么我们要面向接口编程?!

    到底面向?编程 面向过程编程(Procedure Oriented.简称PO) 和 面向对象编程(Object Oriented.简称OO) 我们一定听过,然而实际企业级开发里受用更多的一种编程思想那 ...

  • 设计模式之欢迎来到设计模式世界(二)

    第一节的内容,不知道大家看的如何.小编在博客园的评论里,找到了第一篇的一个缺点,没有把动态改变行为的Duck子类列出来,导致有小伙伴有疑问.在这里说声抱歉,是我疏忽了,好在有GitHub,让大家可以进 ...

  • 封装+继承+多态

    面向对象三大特性 封装: Encapsulation是指一种将抽象性函式接口的实现细节部份包装.隐藏起来的方法.封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随意访问.要访问该类 ...

  • 最简单直接地理解Java软件设计原则之里氏替换原则

    理论性知识 定义 里氏替换原则,Liskov Substitution principle(LSP). 抽象定义是下面这样的 如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义 ...

  • 设计模式-七大软件设计原则

    设计模式 参考资料 图解设计模式 大话设计模式 设计模式之禅 github我见过最好的设计模式 http://c.biancheng.net/view/1326.html 基本原则 开闭原则 在设计的 ...

  • 设计模式-6大设计原则

    单一职责原则(SRP:Single Responsibility Principle) 定义:应该有且仅有一个原因引起类的变更. 优点: 类的复杂性降低: 可读性提高: 可维护性提高: 变更引起的风险 ...

  • 重温设计模式系列(三)面向对象设计原则

    背景 面向对象基础知识,只是给了我们一个概念,如何更好的设计出良好的面向对象代码,需要有设计原则作为支持.设计原则是核心指导思想,在这些原则的基础上,经过不断的实践,抽象,提炼逐步产生了针对特定问题的 ...

  • Java设计模式-软件设计原则

    在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率.节约软件开发成本和维护成本. 1 开闭原则 对扩展开放,对修改 ...

  • 设计模式 - 七大设计原则(四)- 合成复用原则与设计原则总结

    概述 简单介绍一下七大设计原则: 开闭原则:是所有面向对象设计的核心,对扩展开放,对修改关闭 依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体 单一职责原则:一个接口只负责一件事情,只能有一个原因 ...

  • 设计模式 - 七大设计原则(三)- 迪米特法则与里氏替换原则

    概述 简单介绍一下七大设计原则: 开闭原则:是所有面向对象设计的核心,对扩展开放,对修改关闭 依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体 单一职责原则:一个接口只负责一件事情,只能有一个原因 ...

  • 【资料】23种设计模式和六大设计原则

    程序IT圈 www.cxyquan.com 优秀的程序猿技术公众号 1 设计模式的六大原则 ☛开闭原则 对扩展开放,对修改关闭.在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果.简 ...

  • 设计模式——六大设计原则

    文章目录 一.单一职责原则 二.里式替换原则 三.依赖倒置原则 四.接口隔离原则 五.迪米特法则 六.开闭原则 一.单一职责原则 单一职责原则简称 SRP,他想表达的就是字面意思,一个类只承担一个职责 ...