赞
踩
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
目的:降低对象之间的耦合,增加程序的可复用性、可扩展性和可维护性。
访问加限制,函数要节俭,依赖不允许,动态加接口,父类要抽象,扩展不更改。
在程序设计时,我们应该将程序功能最小化,每个类只干一件事。若有类似功能基础之上添加新功能,则要合理使用继承。对于多方法的调用,要会运用接口,同时合理设置接口功能与数量。最后类与类之间做到低耦合高内聚。
设计原则 | 英文全称 | 简称 | 归纳 | 目的 |
---|---|---|---|---|
开闭原则 | Open Closed Principle | OCP | 对扩展开放,对修改关闭 | 降低维护带来的新风险 |
里氏替换原则 | Liskov Substitution Principle | LSP | 不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义 | 防止继承泛滥 |
依赖倒置原则 | Dependence Inversion Principle | DIP | 高层不应该依赖低层,要面向接口编程 | 更利于代码结构的升级扩展 |
单一职责原则 | Single Responsibility Principle | SRP | 一个类只干一件事,实现类要单一 | 便于理解,提高代码的可读性 |
接口隔离原则 | Interface Segregation Principle | ISP | 一个接口只干一件事,接口要精简单一 | 功能解耦,高聚合、低耦合 |
迪米特法则 | Law of Demeter | LoD | 不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度 | 只和朋友交流,不和陌生人说话,减少代码臃肿 |
合成复用原则 | Composite Reuse Principle | CRP | 尽量使用组合或者聚合关系实现代码复用,少使用继承 | 降低代码耦合 |
英文名称:Open Closed Principle
定义:当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。
作用:
实现:抽象约束、封装变化
英文名称:Liskov Substitution Principle
定义:继承必须确保超类所拥有的性质在子类中仍然成立
里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。
子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,不要重写父类的方法。
作用:
实现:
取消原来的继承关系,重新设计它们之间更一般的父类。
英文名称:Dependence Inversion Principle
定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程。依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合。通过面向接口的编程来降低类间的耦合性
作用:
实现:
英文名称:Single Responsibility Principle
定义:一个类应该有且仅有一个引起它变化的原因。
该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:
1)一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力;
2)当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。
作用:
实现:
发现类的不同职责并将其分离,再封装到不同的类或模块中。
英文名称:Interface Segregation Principle
定义:一个类对另一个类的依赖应该建立在最小的接口上。要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
作用:
实现:
英文名称:Law of Demeter
定义:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。
作用:
实现:
英文名称:Composite Reuse Principle
定义:软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。
作用:
通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点。
继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
采用组合或聚合复用时可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点。
它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
实现:
通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。
http://c.biancheng.net/view/1354.html
GoF(Gang of Four) 的 23 种设计模式有两种分类方法,即根据模式的目的来分和根据模式的作用的范围来分。
创建型设计模式 | (5种) | 结构型设计模式 | (7种) | 行为型设计模式 | (11种) |
---|---|---|---|---|---|
工厂模式 | Factory Pattern | 适配器模式 | Adapter Pattern | 策略模式 | Strategy Pattern |
建造者模式 | Builder Pattern | 装饰者模式 | Decorator Pattern | 模板方法模式 | Template Method Pattern |
单例模式 | Singleton Pattern | 代理模式 | Proxy Pattern | 观察者模式 | Observer Pattern |
原型模式 | Prototype Pattern | 外观模式 | Facade Pattern | 迭代器模式 | Iterator Pattern |
桥接模式 | Bridge Pattern | 责任链模式 | Chain of Responsibility Pattern | ||
组合模式 | Composite Pattern | 命令模式 | Command Pattern | ||
享元模式 | Flyweight Pattern | 备忘录模式 | Memento Pattern | ||
状态模式 | State Pattern | ||||
访问者模式 | Visitor Pattern | ||||
中介者模式 | Mediator Pattern | ||||
解释器模式 | Interpreter Pattern |
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。
创建型设计模式(5种) | 英文全称 | 特点 |
---|---|---|
工厂模式 | Factory Pattern | 简单对象 |
建造者模式 | Builder Pattern | 复杂对象 |
单例模式 | Singleton Pattern | 类:对象=1:1 |
原型模式 | Prototype Pattern | 一个对象复制出n个新对象 |
工厂(工厂方法)制造产品(类),厂长抽(抽象模式)了一个人(单例模式)去建造(建造者模式)产品原型(原型模式)
以上 5 种创建型模式,除了工厂方法模式属于类创建型模式,其他的全部属于对象创建型模式。
某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例。
优点 | 缺点 |
---|---|
可以保证内存里只有一个实例,减少了内存的开销 | 一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。 |
可以避免对资源的多重占用 | 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。 |
设置全局访问点,可以优化和共享资源的访问 | 代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。 |
现实比喻:皇帝
构造器私有。
具体实现:把构造函数设置为私有的(private私有访问权限),new自身的实例对象,通过static方法传递出去。
其中new自身的实例对象有两种方式:懒汉式与饿汉式。
类加载时不生成对象,只有当第一次调用 getlnstance 方法时才去创建这个对象。
双重校测锁+volatile禁止指令重排序。
/**
* 懒汉式单例。需要双重加锁机制
* @author liubo
*/
public class SingletonClazz {
/**
* 保证 instance 在所有线程中同步
*/
private static volatile SingletonClazz instance = null;
/**
* private私有化可以避免类在外部被实例化
*/
private SingletonClazz() {
}
public static synchronized SingletonClazz getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new SingletonClazz();
}
return instance;
}
}
类一旦加载就创建一个对象,保证在调用 getInstance 方法之前对象已经存在了。
/**
* 饿汉式单例
* @author liubo
*/
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
/**
* private私有化可以避免类在外部被实例化
*/
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
反射可以破坏单例模式。所以单例模式不安全。
可以用枚举防止反射破坏单例模式。
需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
某类只要求生成一个对象的时候。
某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
频繁访问数据库或文件的对象。
对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
应用 | 特点 |
---|---|
ServletContext | |
ServletConfig | |
ApplicationContext | 获取spring容器中的各种bean组件,注册监听事件,加载配置文件等 |
多线程线程池 | |
数据库连接池 | |
HttpApplication | 所有的HttpModule都共享一个HttpApplication实例 |
按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。
原始社会(没有工厂自给自足)----> 农耕社会(简单工厂。民间酒坊小作坊)---->流水线(工厂方法。自产自销)---->现代产业链(抽象工厂。富士康代工厂,台积电代工厂)
特点:1.在编码时不能预见需要创建哪种类的实例。2.系统不应依赖于产品类实例如何被创建、组合和表达的细节。
特点:1.在编码时不能预见需要创建哪种类的实例。2.系统不应依赖于产品类实例如何被创建、组合和表达的细节。
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
使用JDBC 连接数据库,数据库从MySQL切换到Oracle,需要改动的地方就是切换一下驱动名称。
IOC。
MyBatis。
Factory Method Pattern
定义一个用于创建产品的接口,由子类决定生产什么产品。
Abstract Factory Pattern
提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
建造者模式唯一区别于工厂模式的是针对复杂对象的创建。也就是说,如果创建简单对象,通常都是使用工厂模式进行创建,而如果创建复杂对象,就可以考虑使用建造者模式。
原型模式又叫克隆模式。用一个已经创建的实例对象作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
现实比喻:孙悟空拔下猴毛轻轻一吹就变出很多孙悟空。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
优点 | 缺点 |
---|---|
Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良 | 需要为每一个类都配置一个 clone 方法 |
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作 | clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则 |
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当 |
/**
* 具体原型类
*/
class Realizetype implements Cloneable {
Realizetype() {
System.out.println("具体原型创建成功!");
}
@Override
public Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
return (Realizetype) super.clone();
}
}
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype obj1 = new Realizetype();
Realizetype obj2 = (Realizetype) obj1.clone();
Realizetype obj3 = (Realizetype) obj2.clone();
System.out.println("obj1==obj2?" + (obj1 == obj2));
System.out.println("obj2==obj3?" + (obj2 == obj3));
System.out.println("obj1==obj3?" + (obj1 == obj3));
}
}
对象之间相同或相似,即只是个别的几个属性不同的时候。
创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
应用 | |
---|---|
scope=‘prototype’ | |
JSON.parseObject() | |
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
结构型设计模式(7种) | 英文全称 | 特点 |
---|---|---|
适配器模式 | Adapter Pattern | |
装饰者模式 | Decorator Pattern | |
代理模式 | Proxy Pattern | |
外观模式 | Facade Pattern | |
桥接模式 | Bridge Pattern | |
组合模式 | Composite Pattern | |
享元模式 | Flyweight Pattern |
以上 7 种结构型模式,除了适配器模式分为类结构型模式和对象结构型模式两种,其他的全部属于对象结构型模式。
为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
Spring中的AOP
Struts的Form元素映射
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
优点:
客户端通过适配器可以透明地调用目标接口。
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
在很多业务场景中符合开闭原则。
缺点:
适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
模式的结构与实现
类适配器模式可采用多重继承方式实现,如 C++ 可定义一个适配器类来同时继承当前系统的业务接口和现有组件库中已经存在的组件接口;Java 不支持多继承,但可以定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
类适配器模式的结构图
对象适配器模式的结构图(重点)
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
动态的给对象增加一些职责,即增加其额外的功能。
为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
运用共享技术来有效地支持大量细粒度对象的复用。
将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
行为型设计模式(11种) | 英文全称 | 特点 |
---|---|---|
策略模式 | Strategy Pattern | |
模板方法模式 | Template Method Pattern | |
观察者模式 | Observer Pattern | |
迭代器模式 | Iterator Pattern | |
责任链模式 | Chain of Responsibility Pattern | |
命令模式 | Command Pattern | |
备忘录模式 | Memento Pattern | |
状态模式 | State Pattern | |
访问者模式 | Visitor Pattern | |
中介者模式 | Mediator Pattern | |
解释器模式 | Interpreter Pattern |
以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。
定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
诸葛亮给赵云的三个锦囊 。
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
允许一个对象在其内部状态发生改变时改变其行为能力。
多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。