一、 设计模式的分类
GoF设计模式详解:java设计模式详解
23个GoF设计模式:
- 根据目的分类,可分为创建型、结构型、行为型3类。
- 根据范围分类(模式是用于处理类还是处理对象)可分为类模式、对象模式。
- GoF模式设计表
范围/目的 | 创建型模式 | 结构型模式 | 行为型模式 |
---|---|---|---|
类模式 | 工厂方法模式 | 适配器模式 | 解释器模式、模板方法模式 |
对象模式 | 抽象工厂模式、建造者模式、原型模式、单例模式 | (对象)适配器模式、桥连模式、组合模式、装饰模式、围观模式、享元模式、代理模式 | 职责链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、状态模式、策略模式、访问者模式 |
- 设计模式的优点:
融合了众多专家的经验、是各种最佳实践的集合。
可以简单、方便的使用成功的设计和体系结构。
设计方案更加灵活,易于修改、易于扩展。
提高软件开发的效率和软件质量。
二、 面向对象的设计原则
面向对象设计原则有7个,这些原则蕴含在很多设计模式中,是众多设计模式的指导性原则,并不是强制性的。
1.单一职责原则
一个对象应该只包含单一的职责,并且该职责应该完整的封装在类中。
2.开闭原则
软件实体应该对扩展开放,对修改关闭。
3.里氏替换原则
所有引用基类的地方必须能够用子类对象代替(父类和接口)。
4.依赖倒装原则
针对接口编程,不要针对实现编程,在程序代码中传递参数时或者在关联关系中应该多使用高层次的类,即使用接口和抽象类声明变量、参数类型、方法返回类型以及数据类型转换等,不要用具体类来是做这些事情。
(一个类应该只实现接口或父类中的方法,不然在使用时无法使用在子类中增加的新方法,因为多态不支持)
5.接口隔离原则
客户端不应该依赖那些它不需要的接口;每个接口应该只承担一种职责是独立的,大的接口应该分化成一些小接口的集合。
6.合成复用原则
优先使用对象组合来实现功能,而不是通过继承关系来达到复用的目的。
7.迪米特法则
每一个软件单位对其他单位的影响时很小的,即一个软件实体应该尽可能少的与其他实体发生相互作用,该原则表明一个模块发生改变时应该尽量少的影响其它模块的使用。
三、创建型设计模式
创建型设计模式关注对象的创建,以一种优雅的方式获取对象并对实例化过程进行了抽象,并将对象的创建和使用分离,对用户隐藏了实例创建的过程。
1、工厂方法模式
1)、UML类图:
2)、工厂方法模式包含四个角色:
- Product(抽象产品):定义产品的接口或规则,是所有产品的公共父类(接口)。
- ConcretProduct (具体产品):实现了抽象产品的接口,某种类型的产品专门有对应的工厂制造。
- Factory(抽象工厂):是各种工厂的父类(接口)声明了各种具体工厂的规则,是工厂方法模式的核心。
- ConcreteFactory(具体抽象工厂):是抽象工厂的子类或实现类,实现了定义在抽象父类的方法,可以直接由客户端调用,返回一个具体的Product。实际使用中还可以负责具体产品的初始化,如连接数据库、赋值等功能。
3)、典型代码
- 抽象工厂:
public interface Factory {
public Product factoryMethod();
}
- 具体工厂:
public class ConcreteFactory implements Factory{
@Override
public Product factoryMethod() {
return new ConcreteProduct();
}
}
- 抽像产品类:
public abstract class Product {
//...具体变量和方法
}
- 具体产品类:
public class ConcreteProduct extends Product {
//继承了抽象产品
}
2、抽象工厂模式
1)、概念
提供一个创建一系列相关或相互依赖对象的接口,而无需指定具体的类。
在抽象工厂方法中不会像工厂方法那样一个具体工厂只生产一个类,而是一个工厂方法生产一个产品族,每个产品族有不同的等级结构。
产品族和产品等级结构概念:
- 产品等级结构:
就是产品的继承结构,同属一类的产品。 - 产品族:
同一个工厂生产的所有类型的产品,是不同等级结构的有共同品牌特点的产品。
2)、抽象工厂模式的结构
- AbstractFactory(抽象工厂) : 提供了多个工厂方法用来生产不同类型的产品,这些产品构成了一个产品族。
- ConcreteFactory(具体工厂): 实现了抽象工厂声明的创建产品的方法,生成具体的产品族产品,每个产品都位于一个产品等级结构中。
- 抽象产品: 是某种产品的接口或父类
- AbstractProduct(抽象产品): 为每种产品声明接口,业务方法的抽象类或接口。
- ConcreteProduct(具体产品): 实现或继承了抽象产品类的业务方法,或其他逻辑属性。
3)、uml类图
4)、典型代码
- 产品类(一个产品族):
public abstract class AbstractProductA {
//一个产品等级结构的父类
//...
}
- 具体子类
public class ConcreteProductA extends AbstractProductA{
//...
}
- 抽象工厂:
public interface AbstractFactory {
public AbstractProductA createProductA();//工厂方法1构建产品A
public AbstractProductB createProductB();//工厂方法1构建产品2
}
- 具体工厂
public class ConcreteFactory implements AbstractFactory{
//实现了抽象工厂声明的创建产品族的方法
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA();
}
@Override
public AbstractProductB createProductb() {
return ConcreteProductB();
}
}
3、建造者模式(对象型模式)
1)、概念
将一个复杂对象的构建与它的表示分开,使得同样的构建过程有不同的表示。
建造者模式定义了不同的创建过程,关注如何如何一一步一步的创建产品,具体的建造者相互独立更换或新建新的建造者很方便,符合开闭原则。并且它将部件本身和组装过程分开,用户只需要指定对象的类型就可以得到该对象,无须知道实现细节。
2)、建造者结构
- Builder(抽象建造者):为具体的建造者声明各个部件的建造接口方法,每一个方法都为具体产品构建需要的组件,如buildPartA()\buildPartB(),getResult()用以返回构建完成的对象。可以是接口或类
- ConcreteBuilder(具体建造者):实现了Builder中的方法,为每个方法实现具体的装配逻辑。
- Product(产品): 需要构建的对象,一般有多个内部属性或组成。
- Director(指挥者):和建造者是聚合关系,用来指挥建造者中构建对象的方法的逻辑顺序。
3)、uml类图
4)、典型代码
- 建造者类:
public abstract class Builder {
//子类中构建该产品
protected Product product ;
abstract void buildPartA();//负责构建product的A部分
abstract void buildPartB();//负责构建product的B部分
abstract void buildPartC();//负责构建product的C部分
abstract Product getResult();
}
- 具体建造者类:
package builder;
public class ConcreteBuilder extends Builder{
public ConcreteBuilder() {
this.product = new Product();
}
@Override
void buildPartA() {
this.product.setA();
}
@Override
void buildPartB() {
this.product.setB();
}
@Override
void buildPartC() {
this.product.setC();
}
@Override
Product getResult() {
return this.product;
}
}
- 指导类:
package builder;
public class Director {
Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
- 客户端:
Builder builder = new ConcreteBuilder();
Director dir = new Director(builder);
Product product = dir.conctruct();
...
4、 原型模式
1)、概念
使用原型模式实例指定待创建的对像的类型,并通过赋值这个原型来创建新的对象。
原理:将一个原型对象传给客户端,对象通过自我复制来创建实例。
原型模式有两种:深克隆 和 浅克隆。
浅克隆
如果原型对象的属性值类型是值类型 如int double byte boolean char 等,那么原型对象将把这些值复制一份传给克隆对象, 如果成员变量是引用类型的(对象)那么原型模式将引用对象的地址传给克隆对象,即本身和复制后的对象共用一套引用对象变量。
深克隆
在克隆过程中成员变量无论是值类型还是引用类型,都将复制一份给克隆对象两者的值不是共用一个地址,是分开的。
2)、典型代码
浅克隆:
在java语言中Object类提供了一个clone方法,可以将java对象复制一份,再此之前可以必须实现Coneable接口,表示该对象支持被以浅克隆方式复制。
package prototype;
public class ConcretePrototype implements Cloneable{
//...
@Override
protected Prototype clone() {
Object obj = null;
try {
obj = super.clone();//浅克隆
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return (Prototype) obj;
}
}
Object 的clone方法有以下特点:
- x.clone()!=x 克隆对象和原型对象不是同一个对象。
- x.clone().getClass() == x.getClass() 克隆对象和原型对象是同一类型。
- x.attr == x.clone().attr (引用变量)
- x.varAtrr.equal(x.clone().varAtrr) (值变量)
深克隆
在java中可以通过序列化的方式来实现深克隆,序列化就是将对象写进流的过程,写进流中的对象是原型对象的复制,在内存中仍然存在一个原有的原型对象,序列化不仅可以复制本身,还可以复制其成员变量。
package prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import design.pattern.prototype.deepclone.WeeklyLog;
public class ConcretePrototype implements Serializable{
//...
@Override
public Prototype deepClone() throws IOException, ClassNotFoundException {
//将对象写入流中
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象取出,读入内存
ByteArrayInputStream bio = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bio);
return (Prototype)ois.readObject();
}
}
5、单例模式
1)、概念
确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。
2)、uml类图
3)、饿汉单例
饿汉单例:在定义变量的时候实现单例对象的初始化,
package Singleton;
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public Singleton getInstance() {
return this.instance;
}
}
4)、懒汉单例
懒汉单例:在懒汉单例加载时并不会实例化本身,在调用类方法getInstance时才会初始化,使用到了延迟加载技术。(Lazy Load)
- 懒汉单例线程保护措施1:
- //使用synchronized关键字将方法加锁,使得有多个线程时同时只能一个线程使用单例类
package Singleton;
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
//使用synchronized关键字将方法加锁,使得多个线程时同时只能使用一个对象
synchronized public static Singleton getInstance() {
if (instance==null) {
instance = new Singleton();
}
return instance;
}
}
- 懒汉单例线程保护措施2:
使用synchronized块方法将实例化的代码保护起来,这比措施以更有执行效率。
问题: 有多个线程时人仍然存在创建多个单例的情况如:线程A和B同时进入getInstance方法,均能通过if (instance==null)的判断,由于加锁机制,A拿到了执行权,B处于派对等待状态,只有当A执行完了才能释放锁,此时B又开始执行instance = new LazySingleton();,再次创建了一个对象违背了单例模式的原则。
package Singleton;
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance==null) {
//实例化代码枷锁
synchronized (LazySingleton.class) {
instance = new LazySingleton();
}
}
return instance;
}
}
- 懒汉单例线程保护措施3(解决多次创建单例问题):
package Singleton;
public class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance==null) {
synchronized (LazySingleton.class) {
//再次判断以免再次创建对象
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
5)、静态内部类单例
- 懒汉单例占据内存,饿汉单例控制线程而影响性能,java可以使用Initialization on Demad Holder (IoDH)技术来实现单例模式。
package Singleton;
public class Singleton {
private Singleton() {}
public static class HolderClass{
//静态内部类
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
}
四、结构型模式
行为型模式关注如何将现有的类或对象组织在一起形成更强大的结构,在GoF设计模式种共有7种结构型模式。
结构型模式分为类结构型模式和对象结构型模式。
- 类结构模式关心类之间的组合,由多个类组成更大的系统,类结构模式中由继承和实现两种关系。
- 对象机构模式关心对象之间的组合关系,可通过关联关系来定义一个实例对象,调用实例对象的方法来达到合成复用的目的。(大部都是对象结构型模式)
1、适配器模式(包装器模式)
1)、概念
将一个类的接口转换成客户端希望的另一个接口。适配器模式可以让哪些接口不兼容的类一起工作。
2)、适配器模式的结构
-
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
-
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
-
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
如果没有适配器那么客户端是无法在Target上使用Adaptee的,所以需要一个Adapter类来完成Target和Adapter的适配工作。
2)、UML类图
- 类适配器模式结构图
- 类模式典型代码
class Adapter extends Adaptee implements Target {
public void request() {
specificRequest();
}
}
- 对象适配器模式结构图
- 对象型适配器的典型实例
class Adapter extends Target {
private Adaptee adaptee; //维持一个对适配者对象的引用
public Adapter(Adaptee adaptee) {
this.adaptee=adaptee;
}
public void request() {
adaptee.specificRequest(); //转发调用
}
}
4)、双向适配器
在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。
- 双向适配器典型代码
class Adapter implements Target,Adaptee {
//同时维持对抽象目标类和适配者的引用
private Target target;
private Adaptee adaptee;
public Adapter(Target target) {
this.target = target;
}
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
//可以当作Target用
public void request() {
adaptee.specificRequest();
}
//也可以当作Adaptee用
public void specificRequest() {
target.request();
}
}
2、桥接模式
1)、概念
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
在使用桥接模式时,我们首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。
对于客户端而言,可以针对两个维度的抽象层编程,在程序运行时再动态确定两个维度的子类,动态组合对象,将两个独立变化的维度完全解耦,以便能够灵活地扩充任一维度而对另一维度不造成任何影响。
比如毛笔和颜色:
2)、uml类图
3)、结构
- Abstraction(抽象类):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个Implementor(实现类接口)类型的对象并可以维护该对象,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
- RefinedAbstraction(扩充抽象类):扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
- Implementor(实现类接口):定义实现类的接口,这个接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor接口仅提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作。Implementor接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在Abstraction中不仅拥有自己的方法,还可以调用到Implementor中定义的方法,使用关联关系来替代继承关系。
- ConcreteImplementor(具体实现类):具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplementor对象将替换其父类对象,提供给抽象类具体的业务操作方法。
4)、典型代码
- 第一个维度的接口
在实现Implementor接口的子类中实现了在该接口中声明的方法,用于定义与该维度相对应的一些具体方法。
interface Implementor {
public void operationImpl();
}
- 第二个抽象维度
abstract class Abstraction {
protected Implementor impl; //定义实现类接口对象
public void setImpl(Implementor impl) {
this.impl=impl;
}
public abstract void operation(); //声明抽象业务方法
}
- 在抽象类Abstraction中定义了一个实现类接口类型的成员对象impl,再通过注入的方式给该对象赋值,一般将该对象的可见性定义为protected,以便在其子类中访问Implementor的方法,其子类一般称为扩充抽象类或细化抽象类(RefinedAbstraction)。
class RefinedAbstraction extends Abstraction {
public void operation() {
//业务代码
impl.operationImpl(); //调用实现类的方法
//业务代码
}
}
3、 组合模式
1)、概念
组合多个对象形成树结构以表示具有整体–部分的关系的层次结构。组合模式让客户端可以统一的对待单个对象的组合对象(比如文件和文件夹,他们都是组件对象,客户端针对组件编程)
对于树形结构,当容器对象(如文件夹)的某一个方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员对象(可以是容器对象,也可以是叶子对象)并调用执行,牵一而动百,其中使用了递归调用的机制来对整个结构进行处理。由于容器对象和叶子对象在功能上的区别,在使用这些对象的代码中必须有区别地对待容器对象和叶子对象,而实际上大多数情况下我们希望一致地处理它们,因为对于这些对象的区别对待将会使得程序非常复杂。
组合模式为解决此类问题而诞生,它可以让叶子对象和容器对象的使用具有一致性。
2)、uml类图
3)、组合模式结构
- Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
- Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
- Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
4)、典型代码
- 抽象构建
abstract class Component {
public abstract void add(Component c); //增加成员
public abstract void remove(Component c); //删除成员
public abstract Component getChild(int i); //获取成员
public abstract void operation(); //业务方法
}
- 叶子构建
class Leaf extends Component {
public void add(Component c) {
//异常处理或错误提示
}
public void remove(Component c) {
//异常处理或错误提示
}
public Component getChild(int i) {
//异常处理或错误提示
return null;
}
public void operation() {
//叶子构件具体业务方法的实现
}
}
- 容器构建
class Composite extends Component {
private ArrayList<Component> list = new ArrayList<Component>();
public void add(Component c) {
list.add(c);
}
public void remove(Component c) {
list.remove(c);
}
public Component getChild(int i) {
return (Component)list.get(i);
}
public void operation() {
//容器构件具体业务方法的实现
//递归调用成员构件的业务方法
for(Object obj:list) {
((Component)obj).operation();
}
}
}
4、装饰模式
1)、概念
装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。在装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩充原有类的功能。
2)、装饰者结构
- Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
- ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
- Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
- ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
3)、uml类图
4)、典型代码
- 装饰者的典型代码
class Decorator implements Component{
private Component component; //维持一个对抽象构件对象的引用
public Decorator(Component component) //注入一个抽象构件类型的对象
{
this.component=component;
}
public void operation()
{
component.operation(); //调用原有业务方法
}
}
- 具体装饰者的典型代码
class ConcreteDecorator extends Decorator{
public ConcreteDecorator(Component component){
super(component);
}
public void operation(){
super.operation(); //调用原有业务方法
addedBehavior(); //调用新增业务方法新增属性等
}
//新增业务方法
public void addedBehavior(){
this.component.setA(..);
this.component.setB(..);
}
}
5、外观模式
1)、概念
在软件开发中,有时候为了完成一项较为复杂的功能,一个客户类需要和多个业务类交互,而这些需要交互的业务类经常会作为一个整体出现,由于涉及到的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服务员一样的角色,由它来负责和多个业务类进行交互,而客户类只需与该类交互。外观模式通过引入一个新的外观类(Facade)来实现该功能,外观类充当了软件系统中的“服务员”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。在外观模式中,那些需要交互的业务类被称为子系统(Subsystem)。如果没有外观类,那么每个客户类需要和多个子系统之间进行复杂的交互,系统的耦合度将很大;而引入外观类之后,客户类只需要直接与外观类交互,客户类与子系统之间原有的复杂引用关系由外观类来实现,从而降低了系统的耦合度.
2)、外观模式结构图
3)、外观模式结构
- Facade(外观角色):在客户端可以调用它的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理。
- SubSystem(子系统角色):在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
4)、典型代码
- 子系统
public class SubSystemA{
//业务实现代码
public void method();
}
public class SubSystemB{
//业务实现代码
public void method();
}
public class SubSystemC{
//业务实现代码
public void method();
}
- Facade类典型代码
class Facade{
private SubSystemA obj1 = new SubSystemA();
private SubSystemB obj2 = new SubSystemB();
private SubSystemC obj3 = new SubSystemC();
//各种于子系统交互的方法
public void MethodA(){
obj1.methodA();
obj2.methodB();
obj3.methodC();
}
public void methodB(){
obj1.method1();
}
public void methodC(){
obj2.method();
obj3.method();
}
}
6、 享元模式
1)、概念
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
- 内部状态和外部状态
- 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。如字符的内容,不会随外部环境的变化而变化,无论在任何环境下字符“a”始终是“a”,都不会变成“b”。
- 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。如字符的颜色,可以在不同的地方有不同的颜色,例如有的“a”是红色的,有的“a”是绿色的,字符的大小也是如此,有的“a”是五号字,有的“a”是四号字。而且字符的颜色和大小是两个独立的外部状态,它们可以独立变化,相互之间没有影响,客户端可以在使用时将外部状态注入享元对象中。
2)、享元模式结构
- Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
- ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
- UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
- FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计。当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
3)、uml类图
4)、典型代码
- 享元工厂
class FlyweightFactory {
//定义一个HashMap用于存储享元对象,实现享元池
private HashMap flyweights = newHashMap();
public Flyweight getFlyweight(String key){
//如果对象存在,则直接从享元池获取
if(flyweights.containsKey(key)){
return(Flyweight)flyweights.get(key);
}
//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
else {
Flyweight fw = newConcreteFlyweight();
flyweights.put(key,fw);
return fw;
}
}
}
- 享元类
class Flyweight {
//内部状态intrinsicState作为成员变量,同一个享元对象其内部状态是一致的
private String intrinsicState;
public Flyweight(String intrinsicState) {
this.intrinsicState=intrinsicState;
}
//外部状态extrinsicState在使用时由外部设置,不保存在享元对象中,即使是同一个对象
public void operation(String extrinsicState) {
extrinsicState.method();
}
}
7、代理模式
1)概念
代理模式是常用的结构型设计模式之一,当无法直接访问某个对象或访问某个对象存在困难 时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与 代理对象需要实现相同的接口。由于某些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称之为“代理”的第三者来实现间接访问,该 方案对应的设计模式被称为代理模式。常见的代理形式包括远程代理、保护代理、虚拟代理、缓冲代理、智能引用代理等。
**定义:**给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
2)代理模式的对象结构
- Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使 用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
- Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题 对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代 真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实 主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所 引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中 的操作(在代理主题类中添加了其他相关操作,避免直接操作代理类)。
- RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中 实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
3)uml类图
4)典型代码
//真实主题对象
class RealSubject implements (extends)Subject {
public override void Request() { //业务方法具体实现代码 }
}
//代理主题
class Proxy : Subject {
//维持一个对真实主题对象的引用
private RealSubject realSubject = new RealSubject();
public void PreRequest() { …... }
public override void Request() {
PreRequest();
realSubject.Request();
//调用真实主题对象的方法
PostRequest(); }
public void PostRequest() {
…… }
}
五、行为模式
行为模式关注系统中对象之间的交互作用,研究系统在运行时对象之间的相互通信协作,进一步明确对象的职责。在GoF设计模式中共有11中行为型设计模式,应用于不同场合,用于解决软件设计中的不同问题。