哈尔滨网络开发,成都网站的优化,pageadmin自助建站,北京好的建站团队文章目录 一、设计模式1.1 设计模式的起源1.2 设计模式的定义1.3 记录要素1.4 合理使用模式 二、设计模式的六大原则2.1 开闭原则(Open-Closed Principle, OCP)2.1.1 定义2.1.2 原则分析2.1.3 开闭原则的意义所在 2.2 单一职责原则(Single Responsibility Principle, SRP)2.4.1… 文章目录 一、设计模式1.1 设计模式的起源1.2 设计模式的定义1.3 记录要素1.4 合理使用模式 二、设计模式的六大原则2.1 开闭原则(Open-Closed Principle, OCP)2.1.1 定义2.1.2 原则分析2.1.3 开闭原则的意义所在 2.2 单一职责原则(Single Responsibility Principle, SRP)2.4.1 定义2.4.2 原则分析 2.3 里氏代换原则(Liskov Substitution Principle, LSP)2.3.1 定义2.3.2 原则分析 2.4 依赖倒转原则(Dependency Inversion Principle, DIP)2.4.1 定义2.4.2 原则分析 2.5 合成复用原则(Composite Reuse Principle, CRP)2.5.1 定义2.5.2 原则分析 2.6 迪米特法则(Law of Demeter, LoD)2.6.1 定义2.6.2 原则分析 三、设计模式的分类3.1 创建型模式3.2 结构型模式3.3 行为型模式 参考资料 一、设计模式
1.1 设计模式的起源
软件领域的设计模式起源主要是受到1977年建筑大师Alexander出版的《A Pattern LanguageTowns, Building, Construction》一书。Alexander在其著作中将其建筑行业中的许多问题的最佳解决方案记录为200多种模式其思想不仅在建筑行业影响深远而且很快影响到了软件设计领域。
1987年Kent Beck和Ward Cunningham将Alexander在建筑学上的模式观点应用于软件设计开发了一系列模式并用Smalltalk语言实现了雅致的用户界面。Kent Beck和Ward Cunningham在1987年举行的一次面向对象的会议上发表了论文《在面向对象编程中使用模式》该论文发表后有关软件的设计模式论文以及著作相继出版。
目前被公认在设计模式领域最具影响力的著作是Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides在1994年合作出版的著作:《DesignPatterns: Elements of Reusable Object-OrientedSoftware》中译本《设计模式:可复用的面向对象软件的基础》或《设计模式》该书提出了23种基本设计模式被广大喜爱者昵称为GOF (Gang of Four之书被认为是学习设计模式的必读著作。GOF之书已经被公认为是设计模式领域的奠基之作。
时至今日在可复用面向对象软件发展过程中新的设计模式仍然在涌现丰富着软件设计领域的知识和实践。
1.2 设计模式的定义
设计模式pattern是从许多优秀的软件系统中总结出的成功的可复用的设计方案。 软件模式 •对通用设计问题的重复解决方案 •对真实世界问题的实践的/具体的解决方案 •面向特定的问题环境 •权衡利弊之后得到的“最佳”解决方案 •领域专家和设计老手的“杀手锏” •用文档的方式记录的最佳实践 •在讨论问题的解决方案时一种可交流的词汇 •在使用重用、共享、构造软件系统中一种有效地使用已有的智慧/经验/专家技术的方式 1.3 记录要素
记录一个设计模式需有四个基本要素
1名称 一个模式的名称高度概括该模式的本质有利于该行业统一术语、便于交流使用。
2问题 描述应该在何时使用模式解释设计问题和问题存在的前因后果描述在怎样的环境下使用该模式。
3方案 描述设计的组成部分它们之间的相互关系及各自的职责和协作方式。
4效果 描述模式的应用效果及使用模式应当权衡的问题。主要效果包括使用模式对系统的灵活性、扩充性和复用性的影响。 例如GOF之书如下记录中介者模式 名称 中介者 问题 用一个中介者来封装一系列的对象交互。中介者使各对象不需要显示地相互引用从而使其耦合松散而且可以独立地改变它们之间的交互。 方案 中介者Mediator接口、具体中介者ConcreteMediator、同事Colleague、具体同事ConcreteColleague。 效果 减少了子类的生成、将各个同事解耦、简化了对象协议、控制集中化。 事实上关于如何描述设计模式《设计模式:可复用的面向对象软件的基础》有更多的细节
1.4 合理使用模式
不是软件的任何部分都需要套用模式来设计的必须针对具体问题合理的使用模式。
1. 正确使用
当你设计某个系统并确认所遇到的问题刚好适合使用某个模式就可以考虑使用该模式到你的系统设计中毕竟该模式已经被公认是解决该问题的成功方案能使设计的系统易维护、可扩展性强、复用性好而且这些经典的模式也容易让其他开发人员了解你的系统和设计思想。
2. 避免教条
模式不是数学公式、也不是物理定律、更不是软件设计中的“法律”条文一个模式只是成功解决某个特定问题的设计方案你完全可以修改模式中的部分结构以符合你的设计要求。
3.模式挖掘
模式不是用理论推导出来的而是从真实世界的软件系统中被发现、按着一定规范总结出来的可以被复用的方案。许多文献或书籍里阐述的众多模式实际上都是GOF书中经典模式的变形这些变形模式都经过所谓的“三次规则”即该模式已经在真实世界的三个方案中被成功的采用。可以从某个系统中洞察出某种新模式只要经过“三次规则”就会被行业认可。
4.避免乱用
不是所有的设计中都需要使用模式因为模式不是发明出来的而是总结出来的事实上真实世界中的许多设计实例都没有使用过GOF之书中的经典模式。在进行设计时尽可能用最简单的方式满足系统的要求而不是费尽心机地琢磨如何在这个问题中使用模式一个设计中可能并不需要使用模式就可以很好地满足系统的要求如果牵强地使用某个模式可能会在系统中增加许多额外的类和对象影响系统的性能因为大部分设计模式往往会在系统中加入更多的层这不但增加复杂性而且系统的效率也会下降。
5.了解反模式
所谓反模式就是从某些软件系统中总结出的不好的设计方案反模式就是告诉你如何采用一个不好的方案解决一个问题。既然是一个不好的方案为何还有可能被重复使用呢这是因为这些不好的方案表面上往往有很强的吸引力人们很难一眼就发现它的弊端因此发现一个反模式也是非常有意义的工作。在有了一定的设计模式的基础之后你可以用搜索引擎查找有关反模式的信息这对于学习好设计模式也是非常有帮助的。
二、设计模式的六大原则
2.1 开闭原则(Open-Closed Principle, OCP)
2.1.1 定义
开闭原则定义 一个软件实体应当对扩展开放对修改关闭。即在设计一个模块的时候应当使这个模块可以在不被修改的前提下被扩展即实现在不修改源代码的情况下改变这个模块的行为。
开闭原则由Bertrand Meyer于1988年提出它是面向对象设计中最重要的原则之一。在开闭原则的定义中软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类。
2.1.2 原则分析
事实上完全封闭的系统是不存在的。无论模块怎样实现封闭到最后总还是有一些无法封闭的变化。而对应的思路就是: 既然不能做到绝对封闭那我们就应该选择对哪些变化进行封闭哪些变化进行隔离。然后将那些无法封闭的变化抽象出来进行隔离并且允许扩展尽可能地减少系统的开发。当系统变化出现时要能够及时地作出反应。
开放-封闭原则提供了一个使系统在面对需求变更时,可以保持系统相对稳定的解决方案。其思想简单来说就是面对需求的变化通过添加新的类或者模块等就可以应对而无需对原有的代码进行修改。
2.1.3 开闭原则的意义所在
只依赖于抽象实现开放-封闭原则的核心思想就是 面向抽象编程而不是面向具体编程。
因为抽象相对来说是稳定的。让类去依赖于固定的抽象所以对于修改来说就是封闭的;而通过面向对象的继承以及多态机制可以去实现对抽象体的继承通过重写其方法来改变固有行为从而实现新的扩展方法所以对于来说扩展就是开放的。这是实施开放-封闭原则的基本思路。
同时这种机制也是建立在两个基本的设计原则基础上这就是里氏代换原则和合成复用原则。可以简单地理解为开闭原则是面向对象设计的目标里氏代换原则和依赖倒转原则就是面向对象设计的主要手段。
2.2 单一职责原则(Single Responsibility Principle, SRP)
2.4.1 定义
单一职责原则主要有以下两种定义
一个对象应该只包含单一的职责并且该职责被完整地封装在一个类中。
就一个类而言应该仅有一个引起它变化的原因。
2.4.2 原则分析
一个类或者大到模块小到方法承担的职责越多它被复用的可能性越小而且如果一个类承担的职责过多就相当于将这些职责耦合在一起当其中一个职责变化时可能会影响其他职责的运作。
单一职责原则是实现高内聚、低耦合的指导方针在很多代码重构手法中都能找到它的存在它是最简单但又最难运用的原则需要设计人员发现类的不同职责并将其分离而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。
2.3 里氏代换原则(Liskov Substitution Principle, LSP)
2.3.1 定义
里氏代换原则主要有以下两种定义
如果对每一个类型为S的对象o1都有类型为T的对象o2使得以T定义的所有程序P在所有的对象o1都代换成o2时程序P的行为没有变化那么类型S是类型T的子类型。所有引用基类父类的地方必须能透明地使用其子类的对象
2.3.2 原则分析
里氏代换原则可以通俗表述为: 在软件中如果能够使用基类对象那么一定能够使用其子类对象。把基类都替换成它的子类程序将不会产生任何错误和异常反过来则不成立如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类。
里氏代换原则是实现开闭原则的重要方式之一由于使用基类对象的地方都可以使用子类对象因此在程序中尽量使用基类类型来对对象进行定义而在运行时再确定其子类类型用子类对象来替换父类对象。
2.4 依赖倒转原则(Dependency Inversion Principle, DIP)
2.4.1 定义
依赖倒转原则定义
高层模块不应该依赖低层模块但两者都应该依赖抽象。抽象不应该依赖于细节细节应该依赖于抽象。
可以简单地理解为要针对接口编程,不要针对实现编程。
2.4.2 原则分析
面向对象设计最重要的原则就是创建抽象化同时从抽象化导出具体化具体化给出不同的实现。继承的关系就是一种从抽象化到具体化的实现。抽象层所包含的应该是应用系统的商务逻辑和宏观的、对整个系统来说具有重要的战略性的决定也是必然性的体现。具体的层次含有的是一些次要的与实现有关的算法以及逻辑还有战术性的决定这些都带有相当大的偶然性选择。具体层次的代码都是经常变动的也就无法避免出现错误。
从复用的角度来说高层次的模块是应当被复用的而且会是被复用的重点因为它含有一个应用系统最重要的宏观逻辑并且较为稳定。而在传统的过程性设计当中复用则侧重于具体层次模块的复用。依赖倒转原则正是对传统的过程性设计方法的“倒转”也是高层次模块复用及其可维护性的最有效的规范。
2.5 合成复用原则(Composite Reuse Principle, CRP)
2.5.1 定义
合成复用原则定义尽量使用对象组合而不是继承来达到复用的目的 。 合成复用原则又称为组合/聚合复用原则(Composition/ Aggregate Reuse Principle, CARP) 合成与聚合都是特殊的关联种类。聚合表示比较“弱”的拥有关系具体表现是甲对象中可以包括乙对象但乙对象不是甲对象的一部分;合成则是一种比较“强”的拥有关系体现的是严格的整体与部分之间的关系并且整体与部分有相同的生命周期。比如鱼和鱼群是聚合关系手臂与人体之间则是部分与整体的关系。 优先使用对象的合成/聚合会有助于保持系统的每个类都会被封装并且类被集中在单个任务上。这样类和类之间的继承层次就可以保持比较小的规模并且不太可能增长为不可控制的巨大单位。
2.5.2 原则分析
如果语义上存在着明确的“Is-A”关系并且这种关系是稳定的且不变的则我们考虑使用继承; 如果没有“Is-A”的关系或者这种关系是可变的时我们使用合成。
当两个类都能符合里氏代换原则时这两个类才可以是“Is-A”的关系。也就是说如果两个类之间的关系是“Has-A”但是这两个类被设计为继承则这两个类肯定会违背里氏代换原则。
2.6 迪米特法则(Law of Demeter, LoD)
2.6.1 定义
迪米特法则(Law of Demeter, LoD)常见定义如下
不要和“陌生人”说话只与你的直接朋友通信每一个软件单位对其他的单位都只有最少的知识而且局限于那些与本单位密切相关的软件单位如果两个类之间不必直接通信则这两个类不应该发生直接的相互作用。如果其中的一个类需要调用另一个类的某个方法可以通过第三方来转发这个调用。
迪米特法则的根本思想是强调类之间需要尽量多实现松散耦合。类之间的耦合越弱越有利于复用一个处在弱耦合的类被修改不会对有关系的类造成波及反之则会导致很多麻烦。 信息的隐藏会促进软件的复用。
2.6.2 原则分析
迪米特法则强调的前提是在类的结构设计上每一个类都要尽量降低成员的访问权限类自己包装好自己的private状态不需要让别的类知道的字段或者行为就不要公开。
三、设计模式的分类
根据目的准则模式依据其目的可分为创建型Creational)、结构型(Structural)、或行为型(Behavioral)三种。创建型模式与对象的创建有关;结构型模式处理类或对象的组合;行为型模式对类或对象怎样交互和怎样分配职责进行描述。
3.1 创建型模式
创建型模式抽象了实例化过程。它们帮助一个系统独立于如何创建、组合和表示它的那些对象。一个类创建型模式使用继承改变被实例化的类而一个对象创建型模式将实例化委托给另一个对象。
创建型模式主要包括
工厂方法模式Factory Method Pattern 工厂方法模式定义一个用于创建对象的接口但将实际的对象创建延迟到子类中。这样不同的子类可以创建具体不同类型的对象而客户端代码仅与抽象工厂接口进行交互实现了解耦。它适用于需要根据不同条件创建不同类型对象的情况。抽象工厂模式Abstract Factory Pattern 抽象工厂模式提供一个接口用于创建一系列相关或相互依赖的对象家族而不需要指定具体的类。客户端通过使用抽象工厂接口可以创建和使用这些对象而无需关心具体的实现类。它适用于需要创建一组相关对象的场景。单例模式Singleton Pattern 单例模式确保一个类只有一个实例并提供一个全局访问点来获取该实例。这在需要全局唯一实例的场景下很有用比如配置信息、日志记录器等。建造者模式Builder Pattern 建造者模式将一个复杂对象的构建过程和表示分离使得同样的构建过程可以创建不同的表示。通过使用指挥者来统一组织构建过程客户端可以创建出不同配置的对象。原型模式Prototype Pattern 原型模式通过复制现有的对象来创建新的对象实例而不是通过实例化类来创建。它允许通过克隆的方式来创建新的对象减少了对象创建的开销。
3.2 结构型模式
结构型模式涉及到如何组合类和对象以获得更大的结构以解决对象之间的组合和接口问题。结构型类模式采用继承机制来组合接口或实现。一个简单的例子是采用多重继承方法将两个以上的类组合成一个类结果这个类包含了所有父类的性质。这一模式尤其有助于多个独立开发的类库协同工作。结构型对象模式不是对接口和实现进行组合而是描述了如何对一些对象进行组合从而实现新功能的一些方法。因为可以在运行时刻改变对象组合关系所以对象组合方式具有更大的灵活性,而这种机制用静态类组合是不可能实现的。
结构性模式主要包括
适配器模式Adapter Pattern 适配器模式允许将一个类的接口转换为另一个类的接口以便两者可以一起工作。它在不改变原有类接口的情况下使得原本不兼容的类可以一起协作。装饰器模式Decorator Pattern 装饰器模式允许动态地给对象添加额外的功能而不影响其结构。通过将对象放入装饰器中可以以透明的方式扩展对象的功能。代理模式Proxy Pattern 代理模式用于控制对另一个对象的访问。它在客户端和目标对象之间引入了一个代理对象可以用于增加额外的控制或管理目标对象的访问。外观模式Facade Pattern 外观模式提供了一个简化的接口用于访问复杂系统的一组接口。通过外观模式客户端可以更方便地使用系统而不需要了解底层复杂的实现。桥接模式Bridge Pattern 桥接模式将抽象部分与实现部分分离使得它们可以独立地变化。通过桥接模式可以避免在类之间使用多重继承提高了系统的灵活性。组合模式Composite Pattern 组合模式用于将对象组合成树状结构以表示部分-整体的层次关系。通过组合模式可以使得客户端统一地处理单个对象和组合对象。享元模式Flyweight Pattern 享元模式用于减少对象的创建通过共享相同的对象来降低内存的消耗。它适用于需要创建大量相似对象的场景。
3.3 行为型模式
行为模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式还描述它们之间的通信模式。这些模式刻划了在运行时难以跟踪的复杂的控制流。它们将你的注意力从控制流转移到对象间的联系方式上来。
行为型模式主要包括
观察者模式Observer Pattern 观察者模式定义了对象之间的一对多依赖关系当一个对象的状态发生变化时其所有依赖者都会收到通知并自动更新。这种模式使得对象之间的关联变得松散增加了系统的可维护性和扩展性。策略模式Strategy Pattern 策略模式定义了一系列的算法并使它们可以互相替换使得算法可以独立于客户端而变化。客户端可以根据需要选择使用不同的策略从而实现灵活的算法组织和切换。模板方法模式Template Method Pattern 模板方法模式定义了一个算法的骨架将一些步骤的实现延迟到子类中。这样不同的子类可以实现算法的具体细节而不改变算法的整体结构。命令模式Command Pattern 命令模式将请求封装为一个对象从而使得客户端可以参数化不同的请求并支持对请求进行排队、记录和撤销。这样可以实现请求的发起者和接收者之间的解耦。责任链模式Chain of Responsibility Pattern 责任链模式通过将多个对象组成一条责任链依次处理请求直到请求被处理。这样可以动态地改变请求的处理顺序实现请求发送者与接收者之间的解耦。迭代器模式Iterator Pattern 迭代器模式提供一种顺序访问聚合对象的方法而无需暴露其内部表示。这样可以在不影响聚合对象的情况下遍历聚合对象的元素。备忘录模式Memento Pattern 备忘录模式用于在不破坏封装性的前提下捕获一个对象的内部状态并在对象之外保存这个状态。这样在后续需要时可以恢复对象到之前的状态。
参考资料
《设计模式:可复用的面向对象软件的基础》设计模式奠基之作
《设计模式其实很简单》设计模式的设计原则讲得很清楚