资讯详情

2021-04-08

概述 从许多优秀的软件系统中总结出,设计模式是针对某一类问题的最佳解决方案。

Java中设计模式(java design patterns)通常有23种。  模型可分为创造型、行为型和结构型三类。  创建型模式 创建模式涉及对象的实例化,其特点是不允许用户代码依赖于对象的创建或排列,避免用户直接使用new创建对象。  有五种创造模式:  工厂方法模式、抽象工厂方法模式、生成器模式、原型模式和单例模式。  行为型模式 

行为模式涉及如何合理设计对象之间的交互通信,如何合理分配对象的责任,使设计灵活、易于维护、易于重用。

有以下11种行为模式:  责任链模式、命令模式、解释器模式、迭代器模式、中介模式、备忘录模式、观察者模式、状态模式、策略模式、模板模式和访问者模式。  结构型模式 结构型模式涉及如何组合类和对象以形成更大的结构,和类有关的结构型模式涉及如何合理使用继承机制;和对象有关的结构型模式涉及如何合理的使用对象组合机制。  七种结构模式:  适配器模式、组合模式、代理模式、享元模式、外观模式、桥接模式和装饰模式。  在描述中(加粗字体)介绍了模型中涉及的重要角色。下面逐一介绍。 

Ensure a class only has one instance,and provide a global point of access to it.

确保一个类只有一个例子,并提供一个访问它的全球访问点。  何时使用  当系统需要某一类只有一个例子时 优点  单例模式的唯一例子是由自己控制的,可以很好地控制用户何时访问它。 单例模式的概念很简单,也很常用。  在使用这种模式时,我们应该考虑它是否会在多线程中使用果不用于多线程,写作方法就足够简单了: 

public class SimpleSingleton { private static SimpleSingleton instance; private SimpleSingleton(){} public static SimpleSingleton getIntance(){ if(instance == null) instance = new SimpleSingleton(); return instance; } } 上一个例子是实现懒惰加载模式的简单单例模式。但多线程可能会创建多个例子。以下是多线程的使用。

若上述例子直接应用于多线程,则可直接将其应用于多线程getInstance()设置为同步(synchronized),但是效率不高。任何一个之后,只有一个线程可以调用这种方法,其余的都会排队等待。  所以整个方法做同步不是优解,那就只同步代码块就好了。这导致了双重检查,即在同步块外检查一次null,然后检查同步块。但最终这种方法也会有问题,使用静态内部类是一种更好的方法。  单例模式使用频繁简单,但不一定写对。详情请参考:如何正确写单例模式。详细分析了单例模式的各种写作方法。  在其模式中的示例代码经常使用单例模式,这里没有额外的例子。  这里给出一个推荐的实现方式(枚举): 

public enum EasySingleton{ INSTANCE; }

别名:虚拟结构(Another Name:Virtual Constructor)。

Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclassess.  定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法将一个类的实例化延迟到其子类。  何时使用  用户需要一个子类的例子,但他们不想与子类耦合 用户需要一个子类的例子,但用户不知道哪些子类可用 优点  使用工厂方法可以解耦用户代码和特定类别的子类代码 工厂方法使用户不需要知道它使用的对象是如何创建的,只需要知道对象的方法是什么。 工厂模式简单  介绍工厂方法模式前,先介绍一下工厂模式简单,工厂模式简单也是一种工厂方法模式。 工厂模式简单  介绍工厂方法模式前,先介绍一下工厂模式简单,工厂模式简单也是一种工厂方法模式。  工厂模式简单又称静态工厂方法模式。从命名中可以看出,这种模式一定很简单。它存在的目的很简单:定义一个用于创建对象的界面。  若某些对象(产品)已确定不易改变和添加新产品,则可长期使用简单的工厂模式。以下是简单工厂的例子: 

//演示简单工厂 public class SimpleFactory { public static void main(String args[]) throws Exception{ Factory factory = new Factory(); factory.produce(“PRO5”).run(); factory.produce(“PRO6”).run(); } } //抽象产品 interface MeizuPhone{ void run(); } //具体产品X2 class PRO5 implements MeizuPhone{ @Override public void run() { System.out.println(我是一个PRO5”); } } class PRO6 implements MeizuPhone{ @Override public void run() { System.out.println(我是一个PRO6”); } } //工厂 class Factory{ MeizuPhone produce(String product) throws Exception{ if(product.equals(“PRO5”)) return new PRO5(); else if(product.equals(“PRO6”)) return new PRO6(); throw new Exception(“No Such Class”); } } 很容易看出,简单的工厂模式不容易维护。如果需要添加新产品,则需要修改整个系统。如果我们需要添加,例如PRO7、PRO8等产品,可以直接添加到工程类中。但如果你不知道这个时候还有什么产品,只有在子类实现的时候才知道,你需要工厂的方法模式。

在实际应用中,产品很可能是多层次的树结构。由于简单的工厂模式中只有一个工厂类对应这些产品,实现起来比较麻烦,所以工厂方法模式正式解决了这个问题,下面就来介绍一下工厂方法模式。  工厂方法模式  工厂方法模式去除了简单工厂模式中工厂方法的静态属性,使其能够被子类继承。这样,工厂方法中集中在简单工厂模式中的压力可以由工厂方法模式中的不同工厂子类分担。  对于上述例子,如果使用工厂方法模式,将工厂定义为接口,然后由特定的工厂确定需要生产什么样的产品。为了与简单的工厂进行比较,这里仍然有代码: 

//工厂方法模式 public class FactoryMethod { public static void main(String args[]){ IFactory bigfactory; bigfactory = new SmallFactory(); bigfactory.produce().run(); bigfactory = new BigFactory(); bigfactory.produce().run(); } } //抽象产品 interface MeizuPhone{ void run(); } //具体产品2 class PRO5 implements MeizuPhone{ @Override public void run() { System.out.println(“我是一台PRO5”); } } class MX5 implements MeizuPhone{ @Override public void run() { System.out.println(我是一个MX5”); } } interface IFactory{///抽象工厂 MeizuPhone produce(); } //工厂2 class BigFactory implements IFactory{ @Override public MeizuPhone produce() { return new PRO5(); } } class SmallFactory implements IFactory{ !-- --> @Override public MeizuPhone produce() { return new MX5(); } } 如果了解Java的集合框架,那么它就是一个很好的例子:

Java中的Collection接口的实现都能通过iterator()方法返回一个迭代器,而不同的实现的迭代器都在该实现中以内部类的方式对Iterator接口实现的,然后通过iterator()方法返回。那么,这个iterator()方法就是一种工厂方法。

可以看到,在这里抽象产品是Iterator接口,具体产品就是Collection接口的实现中对Iterator接口的实现,构造者是Collection接口,其提供的工厂方法就是Iterator iterator();,具体构造者就是Collection的实现。而工厂方法模式的结构,也就是由前面加粗的4部分组成。

如果对Java容器不熟悉,下面再提供一个例子(模仿Iterator,其实顺便也介绍了Iterator):

如果有多种数据结构要遍历,我们就需要一种用于遍历不同结构的工具,首先我们就需要为这个工具定义一个接口(抽象产品),用于描述如何来遍历:

//只是需要遍历一堆数据,那么只需要2个方法就可以了 public interface Iterator { boolean hasNext(); //是否还有下一个元素 T next(); //得到下一个元素 } 然后就是我们要遍历的目标,而这些目标此处我们暂定为列表,这就是构造者:

//便于介绍,不做多的操作 public interface List { Iterator iterator(); //返回一个遍历器 boolean add(T t); //添加元素到列表 } 对于List可能有多种实现方式,比如数组和链表,此处就简陋的介绍一下,而这些就是具体构造者,而里面有遍历器的具体实现(具体产品),此处以内部类的形式放到了List的实现(具体构造者)里面,也完全可以修改代码将遍历器的实现(具体产品)独立出来:

数组的实现:

package com.anxpp.designpattern.factorymethod; //方便演示而实现的简陋的数组list public class ArrayList implements List{ private int size; //存放的元素个数,会默认初始化为0 private Object[] defaultList; //使用数组存放元素 private static final int defaultLength = 10;//默认长度 public ArrayList(){ //默认构造函数 defaultList = new Object[defaultLength]; } @Override public Iterator iterator() { return new MyIterator(); } //添加元素 @Override public boolean add(T t) { if(size<=defaultLength){ defaultList[size++] = t; return true; } return false; } //遍历器(具体产品) private class MyIterator implements Iterator{ private int next; @Override public boolean hasNext() { return next<size; } @SuppressWarnings(“unchecked”) @Override public T next() { return (T) defaultList[next++]; } } } 链表实现:

//方便演示而实现的简陋的单向链表list public class LinkList implements List{ private int size; //存放的元素个数,会默认初始化为0 private Node first; //首节点,默认初始化为null @Override public Iterator iterator() { return new MyIterator(); } @Override public boolean add(T t) { if(size==0){ first = new Node(t,null); size++; return true; } Node node = first; while(node.next!=null) node = node.next; node.next = new Node(t,null); size++; return true; } //链表节点 private static class Node{ T data; Node next; Node(T data,Node next){ this.data = data; this.next = next; } } //遍历器 private class MyIterator implements Iterator{ private Node next; //下一个节点 MyIterator(){ next = first; } @Override public boolean hasNext() { return next != null; } @Override public T next() { T data = next.data; next = next.next; return data; } } } 使用上述代码(模式的使用):

package com.anxpp.designpattern.factorymethod; public class TestUse { public static void main(String args[]){ //分别定义两种结构 List array = new ArrayList(); List link = new LinkList(); //添加数据 for(int i = 1;i < 8; i++){ array.add(i); link.add(i); } //获得迭代器 Iterator ai = array.iterator(); Iterator li = link.iterator(); //遍历并输出 while(ai.hasNext()) System.out.print(ai.next()); System.out.println(); while(li.hasNext()) System.out.print(li.next()); } } 控制台会输出: 1234567 1234567 这就是工厂方法模式,其中遍历器也算是一种迭代器设计模式,后面会介绍。我不会跟你讲什么造车,造轮船,造人,我会给出实际应用。这里只是其中一种应用的举例,当一个接口的一系列实现需要另外的对象对其进行相同操作时,我们就可以这样用:在这个接口中定义返回另外一个对象的方法(工厂方法),然后再在这个接口的实现中,返回对其操作的对象。

上面这个例子会在迭代器模式中给出完整的实现代码。

一抽象产品类派生出多个具体产品类;一抽象工厂类派生出多个具体工厂类;每个具体工厂类只能创建一个具体产品类的实例。 即定义一个创建对象的接口(即抽象工厂类),让其子类(具体工厂类)决定实例化哪一个类(具体产品类)。“一对一”的关系。

与简单工厂间的取舍:工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。 反过来讲,简单工厂模式是由工厂方法模式退化而来。设想如果我们非常确定一个系统只需要一个实的工厂类, 那么就不妨把抽象工厂类合并到实的工厂类中去。而这样一来,我们就退化到简单工厂模式了。

可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。

如果再分得详细一点,一个工厂可能不只是生产手机(如小米除了手机,连电饭锅都有),但有得工厂智能生成低端的产品,而大一点的工厂可能通常是生成更高端的产品。所以一个工厂是不够用了,这时,就应该使用抽象工厂来解决这个问题。

别名:配套(Another Name:Kit)

Provide an interface for creating families of related or dependent objects without specifying their concrete classess.

提供一个创建一系列或相互依赖对象的接口,而无须指定他们的具体的类。

何时使用:

优点: 

上述生成魅族产品的例子中,我们只生产了手机,但是它不止有手机一种产品,可能还有其他的,比如耳机,为了还可以生成耳机,我们需要对上例进行扩展。

我们先给出上面生成手机的例子的扩展后的抽象工厂模式代码,以比较这几种模式:

//抽象工厂模式 public class AbstractFactory { public static void main(String args[]){ IFactory bigfactory = new BigFactory(); IFactory smallfactory = new BigFactory(); bigfactory.producePhone().run(); bigfactory.produceHeadset().play(); smallfactory.producePhone().run(); smallfactory.produceHeadset().play(); } } //抽象产品2 interface Headset{ void play(); } //抽象产品 interface MeizuPhone{ void run(); } //具体产品22 class PRO5 implements MeizuPhone{ @Override public void run() { System.out.println(“我是一台PRO5”); } } class MX5 implements MeizuPhone{ @Override public void run() { System.out.println(“我是一台MX5”); } } class EP21 implements Headset{ @Override public void play() { System.out.println(“我是一副EP21”); } } class EP30 implements Headset{ @Override public void play() { System.out.println(“我是一台EP30”); } } //抽象工厂 interface IFactory{ MeizuPhone producePhone(); Headset produceHeadset(); } //具体工厂2 class BigFactory implements IFactory{ @Override public MeizuPhone producePhone() { return new PRO5(); } @Override public Headset produceHeadset() { return new EP30(); } } //具体工厂*2 class SmallFactory implements IFactory{ @Override public MeizuPhone producePhone() { return new MX5(); } @Override public Headset produceHeadset() { return new EP21(); } } 在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式(不如上例减去耳机这种产品,就回到工厂方法模式了)。

这样举例子其实很空洞,这里只是为了比较三种模式,给出抽象的例子才更容易看出区别。

那么上例中实际应用就是生产迭代器的例子,这里也对齐扩展来介绍抽象工厂模式。Iterator迭代器是Collection专属的,但是现在我们希望能生产Map的迭代器,我们都知道,Map不是继承自Collection的,遍历的方式是不一样的,这就相当于2个产品族,接下来我们就要来实现它。

为了演示我们如果实现这个同时能生产Map和Collection的迭代器,我会将例子一步步贴出来:

首先是抽象产品,用来描述迭代器的公共接口:

//抽象产品 public interface IIterator { boolean hasNext(); Object next(); } 然后是抽象工厂,用来返回不同迭代器:

//抽象工厂 public interface IIteratorFactory { IIterator iteratorMap(Map<T,Object> m); IIterator iteratorCollection(Collection c); } 接下来是具体产品。

Collection的迭代器(具体产品):

//具体产品,Collection迭代器(用到了代理模式) public class IteratorCollection implements IIterator{ Iterator iterator; public IteratorCollection(Collection c){ iterator = c.iterator(); } @Override public boolean hasNext() { return iterator.hasNext(); } @Override public T next() { return iterator.next(); } } Map的迭代器(具体产品):

//具体产品,Map迭代器(用到了代理模式) public class IteratorMap implements IIterator{ Iterator<Map.Entry<T, Object>> iterator; public IteratorMap(Map<T,Object> m){ iterator = m.entrySet().iterator(); } @Override public boolean hasNext() { return iterator.hasNext(); } @Override public Object next() { return iterator.next().getValue(); } } 完成具体产品设计后,我们就要实现具体的工厂了:

//具体工厂 public class IteratorFactory implements IIteratorFactory{ @Override public IteratorMap iteratorMap(Map<T,Object> m) { return new IteratorMap(m); } @Override public IteratorCollection iteratorCollection(Collection c) { return new IteratorCollection©; } } 至此,这个小框架就完成了,我们可以使用它来遍历Collection(List,Set,Queue都是集成自它)和Map:

//测试使用 public class TestUse { public static void main(String args[]){ IIteratorFactory factory = new IteratorFactory<>(); Collection collection = new ArrayList(); Map<Integer, Object> map = new LinkedHashMap<>(); for(int i=0;i<10;i++){ collection.add(i); map.put(i, i); } IIterator iteratorCollection = factory.iteratorCollection(collection); IIterator iteratorMap = factory.iteratorMap(map); while(iteratorCollection.hasNext()) System.out.print(iteratorCollection.next()); System.out.println(); while(iteratorMap.hasNext()) System.out.print(iteratorMap.next()); } } 输出:

0123456789 0123456789 实际情况下,我们可能不应该这么做,以为Collection面向一种对象的容器,Map是面向两种对象的关联容器,但是此例使用抽象工厂模式确实实现了不同容器的 统一遍历方式。

如果一个容器持有的大量对象,他们都直接或间接集成自某一个类,使用访问者模式遍历也是一种很好的方式,具体在后面的访问者模式中会详细介绍。

工厂模式主要就涉及上面介绍的三种:

简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。
工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。

何时使用:

当系统准备为用户提供一个内部结构复杂的对象,而且在构造方法中编写创建该对象的代码无法满足用户需求时,就可以使用生成器模式老构造这样的对象。
当某些系统要求对象的构造过程必须独立于创建该对象的类时。
优点:

生成器模式将对象的构造过程封装在具体的生成器中,用户使用不同的具体生成器就可以得到该对象的不同表示。
生成器模式将对象的构造过程从创建该对象的类中分离出来,使用户无须了解该对象的具体组件。
可以更加精细有效的控制对象的构造过程。生成器将对象的构造过程分解成若干步骤,这就是程序可以更加精细,有效的控制整个对象的构造。
生成器模式将对象的构造过程与创建该对象类解耦,是对象的创建更加灵活有弹性。
当增加新的具体的生成器是,不必修改指挥者的代码,即该模式满足开-闭原则。
模式的重心在于分离构建算法和具体的构造实现,从而使构建算法可以重用。

比如我们要得到一个日期,可以有不同的格式,然后我们就使用不同的生成器来实现。

首先是这个类(产品):

//产品 public class MyDate { String date; } 然后就是抽象生成器,描述生成器的行为:

//抽象生成器 public interface IDateBuilder { IDateBuilder buildDate(int y,int m,int d); String date(); } 接下来是具体生成器,一个以“-”分割年月日,另一个使用空格:

//具体生成器 public class DateBuilder1 implements IDateBuilder{ private MyDate myDate; public DateBuilder1(MyDate myDate){ this.myDate = myDate; } @Override public IDateBuilder buildDate(int y, int m, int d) { myDate.date = y+"-"+m+"-"+d; return this; } @Override public String date() { return myDate.date; } }

//具体生成器 public class DateBuilder2 implements IDateBuilder{ private MyDate myDate; public DateBuilder2(MyDate myDate){ this.myDate = myDate; } @Override public IDateBuilder buildDate(int y, int m, int d) { myDate.date = y+" “+m+” "+d; return this; } @Override public String date() { return myDate.date; } } 接下来是指挥官,向用户提供具体的生成器:

//指挥者 public class Derector { private IDateBuilder builder; public Derector(IDateBuilder builder){ this.builder = builder; } public String getDate(int y,int m,int d){ builder.buildDate(y, m, d); return builder.date(); } } 使用如下:

public class TestUse { public static void main(String args[]){ MyDate date = new MyDate(); IDateBuilder builder; builder = new DateBuilder1(date).buildDate(2066, 3, 5); System.out.println(builder.date()); builder = new DateBuilder2(date).buildDate(2066, 3, 5); System.out.println(builder.date()); } } 输出:

2066-3-5 2066 3 5 使用不同生成器,可以使原有产品表现得有点不一样。

往往在实际的应用中,生成器要做的工作不会这么简单,而是相对复杂的(因为其产品一般是比较复杂的),原有构建的维护会转移到生成器的维护上。

Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.

用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

何时使用:

程序需要从一个对象出发,得到若干个和其状态相同,并可独立变化其状态的对象时。
当对象的创建需要独立于它的构造过程和表示时。
一个类创建实例状态不是很多,那么就可以将这个类的一个实例定义为原型,那么通过该实例复制该原型得到新的实例可能比重新使用类的构造方法创建新实例更方便
优点:

当创建类的新实例的代价更大时,使用原型模式复制一个已有的实例可以提高创建新实例的效率。
可以动态的保存当前对象的状态。在运行时,可以随时使用对象流保存当前对象的一个复制品。
可以在运行时创建新的对象,而无须创建一系列类和集成结构。
可以动态的添加、删除原型的复制品。
原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。

例子中的抽象原型没有使用方法名clone(),其原因下面会介绍。

简单形式的原型模式:

//具体原型 public class SimplePrototype implements Prototype,Cloneable { int value; //clone()实现 @Override public Object cloneSelf() { SimplePrototype self = new SimplePrototype(); self.value = value; return self; } //使用 public static void main(String args[]){ SimplePrototype simplePrototype = new SimplePrototype(); simplePrototype.value = 500; SimplePrototype simplePrototypeClone = (SimplePrototype) simplePrototype.cloneSelf(); System.out.println(simplePrototypeClone.value); } } //抽象原型 interface Prototype{ Object cloneSelf();//克隆自身的方法 } //客户端使用 class Client{ SimplePrototype prototype; public Client(SimplePrototype prototype){ this.prototype = prototype; } public Object getPrototype(){ return prototype.cloneSelf(); } } 简单的原型模式就是在clone()实现时,new一个新的实例,然后为成员变量赋值后返回。

Java的原生支持

Java中所有类都直接或间接继承自Object类,Object类中已有clone()方法:”protected native Object clone() throws CloneNotSupportedException;“,可以看到权限是protected的,所以仅有子类可以访问这个方法,但我们可以在子类中重写这个方法,将访问权限上调到public,然后方法体里面return super.clone()。

我们能看到这个Object方法是可能会抛出异常的,我们必须实现Cloneable接口,才可以使用这个方法,否则会抛出“java.lang.CloneNotSupportedException”的异常。这个Cloneable接口其实是空的,实现它的目的在于让JVM知道这个对象是可以可复制的,否则clone()时就会发生异常。下面看演示代码:

//使用 java 自带的支持 public class APITestUse { public static void main(String args[]) throws CloneNotSupportedException{ MyObject myObject = new MyObject(); myObject.i = 500; MyObject myObjectClone = (MyObject) myObject.clone(); System.out.println(myObjectClone.i); } } //一个可以复制的对象 class MyObject implements Cloneable{ int i; public Object clone() throws CloneNotSupportedException{ return super.clone(); } }//结果会输出 500 调用这个方法时,成员变量会自动被复制。所以如果需要使用原型模式,Java原生支持就已经很好用了。

除了以上的原生支持,java中还有一种序列化,只需要对象实现Serializable接口。这样,我们可以将对象写入到流中,可以保存到文件,也可以通过网络发送到其他地方:

//使用Serializable支持克隆 public class SerializablePrototype implements Serializable{ private static final long serialVersionUID = 1L; private int i; private transient int notClone;//transient关键字的成员不会被序列化 public int getI() { return i; } public void setI(int i) { this.i = i; } public int getNotClone() { return notClone; } public void setNotClone(int notClone) { this.notClone = notClone; } public void writeToFile(String path) throws Exception{ FileOutputStream outStream = new FileOutputStream(path); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outStream); objectOutputStream.writeObject(this); outStream.close(); } public SerializablePrototype ReadFromFile(String path) throws Exception{ File file = new File(path); if(!file.exists()) file.createNewFile(); FileInputStream inStream = new FileInputStream(path); ObjectInputStream objectOutputStream = new ObjectInputStream(inStream); Object o= objectOutputStream.readObject(); inStream.close(); return (SerializablePrototype) o; } public static void main(String args[]) throws Exception{ String path = “D:/SerializablePrototype.instance”; SerializablePrototype prototype = new SerializablePrototype(); prototype.setI(123); prototype.setNotClone(456); prototype.writeToFile(path); SerializablePrototype prototypeClone = new SerializablePrototype(); prototypeClone = prototype.ReadFromFile(path); System.out.println(prototypeClone.getI() + " " + prototypeClone.getNotClone()); } }//输出:123 0 我们来分析上例:这个对象有3个成员变量,而其中一个是有transient关键字申明的,一个是序列化id,一个是普通变量,在main方法中,想创建了对象,并设置值,然后写入到文件,再从文件读出来,最后输出读出来的对象的变量,普通变量是可以正常输出的(序列化id也可以,只是此处没有输出来而已),而transient申明的变量为0了,那就证明这个变量没有被保存到文件中,因为这个关键字声明的变量在序列化时会被忽略,而是后来创建时被自动初始化为0了(java中类的成员变量是基本数据类型时,如果没有初值,就会被自动初始化为0)。

额...这里是介绍模式,好像说得多了点,那原型模式就介绍到这儿了。

使很多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

何时使用

有许多对象可以处理用户请求,希望程序在运行期间自动确定处理用户的那个对象。
希望用户不必明确指定接收者的情况下,想多个接受者的一个提交请求
程序希望动态的指定可处理用户请求的对象集合
优点

低耦合
可以动态的添加删除处理者或重新指派处理者的职责
可以动态改变处理者之间的先后顺序
通常来说,一个纯粹的责任链是先传给第一个处理,如果处理过了,这个请求处理就此结束,如果没有处理,再传给下一个处理者。

比如我们有一个数学公式,有一个整数输入,要求小于0时返回绝对值,其次,小于10的时候返回他的二次幂,否则,返回他本身:

首先我们要定义一个接口(处理者),来描述他们共有的行为:

//处理者 public interface Handler { int handleRequest(int n); void setNextHandler(Handler next); } 然后是具体的处理者(3个):

//第一个具体处理者,处理小于0的 public class Handler1 implements Handler { private Handler next; @Override public int handleRequest(int n) { if(n<0) return -n; else{ if(next==null) throw new NullPointerException(“next 不能为空”); return next.handleRequest(n); } } @Override public void setNextHandler(Handler next) { this.next = next; } }

//第二个具体处理者,处理>=0但小于10的 public class Handler2 implements Handler { private Handler next; @Override public int handleRequest(int n) { if(n<10) return n*n; else{ if(next==null) throw new NullPointerException(“next 不能为空”); return next.handleRequest(n); } } @Override public void setNextHandler(Handler next) { this.next = next; } }

//第三个具体处理者,处理>=0但小于10的 public class Handler3 implements Handler { private Handler next; @Override public int handleRequest(int n) { if(n<=Integer.MAX_VALUE) return n; else{ if(next==null) throw new NullPointerException(“next 不能为空”); return next.handleRequest(n); } } @Override public void setNextHandler(Handler next) { this.next = next; } } 使用:

public class TestUse { public static void main(String args[]){ Handler h1,h2,h3; h1 = new Handler1(); h2 = new Handler2(); h3 = new Handler3(); h1.setNextHandler(h2); h2.setNextHandler(h3); System.out.println(h1.handleRequest(-1)); System.out.println(h1.handleRequest(5)); System.out.println(h1.handleRequest(9999)); } } 此处责任链中的具体处理者的顺序是不能重设的,否则可能会引发错误,但更多的情况是完全可以随意更改他们的位置的,就上例中,只要把if中的条件重新设置(各自独立,不相互依赖),就可以了。

我们写java web程序的时候,通常会编写一些过滤器(Filter),然后配置到web.xml中,这其实就是责任链模式的一种实践。而使用Log4j记录日志,配置级别的时候,也同样用到了责任链模式。

我们使用责任链模式的时候,不一定非得某一处理者处理后就得终止请求的传递,如果有其他需求,我们依然可以继续传递这个请求到下一个具体的处理者。

别名:动作,事物(Another Name:Action,Transaction)

Encapsulate a request as an object,thereby letting you parameterize clients with different reauests,queue or log requests,and support undoable operations.

将一个请求封装为一个对象,从而使用户可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

何时使用:

程序需要在不同的时刻指定、排列和执行请求。
程序需要提供撤销操作。
程序需要支持宏操作。
优点:

在命令模式中,请求者(Invoker)不直接与接受者(Receiver)交互,及请求者(Invoker)不包含接受者(Receiver)的引用,因此彻底消除了彼此间的耦合。
命令模式满足“开-闭原则”。如果增加新的具体命令和该命令的接受者,不必修改调用者的代码,调用者就可以使用新的命令对象;反之,如果增加新的调用者,不必修改现有具体命令和接收者,新增加的调用者就可以使用已有的具体命令。
由于请求者的请求被封装到具体的命令中,那么就可以将具体命令保存到持久化的媒介中,在需要的时候,重新执行这个具体命令。因此,使用命令模式可以记录日志。
使用命令模式可以对请求者的“请求”进行排队。每个请求都各自对应一个具体命令,因此可以按一定顺序执行这些具体命令。
一个对象有多种操作,但是我们不希望调用者(请求者)直接使用,我们就额外添加一个对象,然后让调用者通过这个对象来使用那些操作。

比如,我们有一个类可以在磁盘上新建或是删除文件(接收者),但是我们不希望直接提供给别人(请求者)使用,所以我们就为它的各种操作创建对应的命令,下面我们用代码来实现这个需求:

接收者,可以在磁盘删除或新建文件:

//接收者 public class MakeFile { //新建文件 public void createFile(String name) throws IOException{ File file = new File(name); file.createNewFile(); } //删除文件 public boolean deleteFile(String name){ File file = new File(name); if(file.exists()&&file.isFile()){ file.delete(); return true; } return false; } } 然后就是执行操作的接口:

//命令接口 public interface Command { void execute(String name) throws Exception; } 我们需要创建具体的命令,这里就是2个,新建和删除:

//新建文件命令 public class CommandCreate implements Command { MakeFile makeFile; public CommandCreate(MakeFile makeFile) { this.makeFile = makeFile; } @Override public void execute(String name) throws Exception { makeFile.createFile(name); } }

//删文件命令 public class CommandDelete implements Command{ MakeFile makeFile; public CommandDelete(MakeFile makeFile) { this.makeFile = makeFile; } @Override public void execute(String name) throws Exception { makeFile.deleteFile(name); } } 最后就是请求者了:

//请求者 public class Client { Command command; public Client setCommand(Command command){ this.command = command; return this; } public void executeCommand(String name) throws Exception{ if(command==null) throw new Exception(“命令不能为空!”); command.execute(name); } } 这样,我们就可以使用了,方式如下:

public class TestUse { public static void main(String args[]) throws Exception{ //接收者 MakeFile makeFile = new MakeFile(); //命令 CommandCreate create = new CommandCreate(makeFile); CommandDelete delete = new CommandDelete(makeFile); //请求者 Client client = new Client(); //执行命令 client.setCommand(create).executeCommand(“d://test1.txt”); client.setCommand(create).executeCommand(“d://test2.txt”); client.setCommand(delete).executeCommand(“d://test2.txt”); } }//执行完后在D盘会有一个test1.txt的文件,test2.txt本页创建了,不过又被删除了。。 这里只是简单的实现,诸如CommandCreate命令的操作,如果我们需要undo的,那么就需要在命令接口中添加undo()方法并在具体命令中实现即可(将操作保存到栈里即可,undo的时候出栈并撤销操作)。

命令模式不宜滥用,比如:使用这种模式,会多出来很多对象(命令)。

命令模式中还有一种具体命令叫宏命令,它会包含一些其他具体命令的引用,执行宏命令可以执行这个宏命令所包含的引用的命令,概念清楚后实现起来也是容易的:

比如输出文章的命令,有中文输出命令、英文输出命令和宏命令,宏命令包含了其他两个命令的引用(可以使用列表保存这些命令),如果执行宏命令,宏命令会一次执行它所包含引用的其他命令(迭代命令列表并执行即可)。

Given a language,define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

何时使用

当有一个简单的语言需要解释执行,并且可以将该语言的每一个规则表示为一个类时,就可以使用解释器模式。
优点

将每一个语法规则表示成一个类,方便与实现简单的语言。
由于使用类表示语法的规则,可以较容易改变或扩展语言的行为。
通过在类结构中加入新的方法,可以在解释的同时增加新的行为。
概念其实很简单。在有些问题上,我们可能希望自定定义简单的语言来描述,然后我们自己能解释它。

解释器模式一般包括四种角色:

抽象表达式:该角色为一个接口,负责定义抽象的解释操作。
终结符表达式:实现抽象表达式接口的类。
非终结表达式:也是实现抽象表达式的类。
上下文(Context):包含解释器之外的一些全局信息。
使用该模式设计程序一般需要三个步骤:

解析语句中的动作标记。
将标记规约为动作。
执行动作。
这种模式一般会应用到一些特殊的问题上,使用这种模式一般需要了解形式语言中的基本知识。js内核就是一个强大的解释器。

简单的解释器模式,我们需要解释出来表达式的信息即可;而更深一层的,我们需要把表达式中的内容,翻译成我们程序运行的一部分来执行。

本初不提供例子,理解概念即可。有需求的时候再深入学习。如果确实需要举例,请在回复中说明,我会更新文章并添加一些内容。

别名:游标(Another Name:Cursor)

提供一种方法顺序访问一个聚合对象中的各个元素,而由不需要暴露该对象的内部细节。

何时使用

让用户访问集合汇总的对象而不想暴露这个集合的实现时
对不同集合提供一个统一的遍历接口时
优点

用户使用迭代器访问集合中的对象而不需要知道这个集合的具体实现
可以同时使用多个迭代器遍历一个集合
通常容器提供的迭代器时可以高速遍历它本身的,而使用其本身的机制(如LinkedList中使用get(i)方法遍历)遍历性能可能并不好。

其实这个在工厂方法模式给出的例子就足够解释这个模式的使用了,如需看具体代码实现,请移步工厂方法模式中的例子查看。

其中主要的角色是集合、具体集合、迭代器、具体迭代器。

迭代器其实很简单,下面我们就继续工厂方法模式中的例子,将它完善一下:

稍微增强的集合接口:

//集合接口 public interface MyList { MyIterator iterator(); //返回一个遍历器 boolean add(T t); //添加元素到列表 T get(int index); //得到元素 T remove(); //删除最后一个元素 boolean remove(T element); //删除指定元素 T remove(int index); //删除指定位置元素 boolean set(int index,T element); //修改指定位置值 int size(); } 容量可以自动增长的数组实现的集合:

public class MyArrayList implements MyList{ private int size; //存放的元素个数,会默认初始化为0 private Object[] defaultList; //使用数组存放元素 private static final int defaultLength = 10;//默认长度 public MyArrayList(){ //默认构造函数 defaultList = new Object[defaultLength]; } @Override public MyIterator iterator() { return new Iterator(); } //大小自动增长 private void ensureCapacity(int capacity){ int nowLength = defaultList.length; if(capacity >= nowLength){ nowLength = nowLength + (nowLength>>1); if(nowLength<0)//溢出 nowLength = Integer.MAX_VALUE; defaultList = Arrays.copyOf(defaultList, nowLength); } } //添加元素 @Override public boolean add(T t) { ensureCapacity(size+1); defaultList[size++] = t; return true; } //获取元素 @SuppressWarnings(“unchecked”) @Override public T get(int index) { if(index<0 || index>=size) return null; return (T) defaultList[index]; } @Override public T remove() { return remove(size-1); } @SuppressWarnings(“unchecked”) @Override public T remove(int index) { if(index<0||index>=size) return null; T element = (T) defaultList[index]; if(index != size-1) System.arraycopy(defaultList, index+1, defaultList, index,size-index-1); size–; return element; } @Override public boolean remove(T element) { if(element==null){ for(int i = 0 ; i<size;i++) if(defaultList[i]==null){ System.arraycopy(defaultList, i+1, defaultList, i,size-i-1); size–; return true; } } else{ for(int i = 0 ; i<size;i++) if(defaultList[i].equals(element)){ System.arraycopy(defaultList, i+1, defaultList, i,size-i-1); size–; return true; } } return false; } @Override public boolean set(int index,T element) { if(index<0||index>=size) return false; defaultList[index] = element; return true; } @Override public int size() { return size; } //迭代器 private class Iterator implements MyIterator{ private int next; @Override public boolean hasNext() { return next<size; } @SuppressWarnings(“unchecked”) @Override public T next() { return (T) defaultList[next++]; } @Override public T remove() { // TODO Auto-generated method stub return null; } } } 链表实现的集合:

public class MyLinkedList implements MyList{ private int size; //存放的元素个数,会默认初始化为0 private Node first; //首节点,默认初始化为null @Override public MyIterator iterator() { return new Iterator(); } @Override public boolean add(T t) { if(size0){ first = new Node(t,null); size++; return true; } Node node = first; while(node.next!=null) node = node.next; node.next = new Node(t,null); size++; return true; } @Override public T get(int index) { Node node = first; while(–index>=0) node = node.next; return node.data; } @Override public T remove() { return remove(size-1); } @Override public T remove(int index) { if(index<0||index>=size) return null; Node node = first; while(–index>0) node = node.next; T element = node.next.data; node.next = node.next.next; size–; return element; } @Override public boolean remove(T element) { if(element == null){ if(first.datanull){ first = first.next; size–; return true; } Node node = first; do{ if(node.next.data==null){ node.next = node.next.next; size–; return true; } node = node.next; } while(node.next!=null); } else{ if(first.data.equals(element)){ first = first.next; size–; return true; } Node node = first; do{ if(node.next.data.equals(element)){ node.next = node.next.next; size–; return true; } node = node.next; } while(node.next!=null); } return false; } @Override public boolean set(int index, T element) { if(index<0||index>=size) return false; Node node = first; while(–index>0) node = node.next; node.data = element; return true; } @Override public int size() { return size; } //链表节点 private static class Node{ T data; Node next; Node(T data,Node next){ this.data = data; this.next = next; } } //遍历器 private class Iterator implements MyIterator{ private Node next; //下一个节点 Iterator(){ next = first; } @Override public boolean hasNext() { return next!=null; } @Override public T next() { T data = next.data; next = next.next; return data; } @Override public T remove() { // TODO Auto-generated method stub return null; } } } 迭代器接口:

public interface MyIterator { boolean hasNext(); //是否还有下一个元素 T next(); //得到下一个元素 T remove(); } 具体的迭代器就是集合具体实现里面的迭代器内部类,下面是使用 :

public class TestUse { public static void main(String args[]){ //分别定义两种结构 MyList array = new MyArrayList(); MyList link = new MyLinkedList(); //添加数据 for(int i = 1;i < 15; i++){ array.add(i+""); link.add(i+""); } //数组操作 System.out.println(array.remove()); System.out.println(array.get(5)); System.out.println(array.remove(5)); System.out.println(array.get(5)); System.out.println(array.remove(“7”)); System.out.println(array.set(0, “00”)); //使用迭代器 MyIterator ai = array.iterator(); while(ai.hasNext()) System.out.print(ai.next()+" “); System.out.println(); System.out.println(link.remove()); System.out.println(link.get(5)); System.out.println(link.remove(5)); System.out.println(link.get(5)); System.out.println(link.remove(“7”)); System.out.println(link.set(0, “00”)); //使用迭代器 MyIterator li = link.iterator(); while(li.hasNext()) System.out.print(li.next()+” "); System.out.println(); System.out.println(“a size=” + array.size()); System.out.println(“l size=” + link.size()); } } 输出:

14 6 6 7 true true 00 2 3 4 5 8 9 10 11 12 13 14 6 6 7 true true 00 2 3 4 5 8 9 10 11 12 13 a size=11 l size=11 这里的迭代器就是典型的迭代器模式的实现(不过此处的迭代器没有实现remove()方法,查找集合的remove()方法实现也简单),介绍得有点多了,把集合都给介绍了…

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变他们之前的交互。

何时使用

优点

两个类直接关联,是很好实现的,但如果不希望两个类直接发生交互,那么就需要使用中介者模式了。

比如有两个类,他们都是做持久化的,一个负责将数据写入文件,一个负责将数据写入数据库。他们谁先接收到数据是不确定的,但是要确保其中一个接收到数据后,另外一个也必须完成这些数据的持久化。如果我们直接将两个类关联在一起,互相调用是可以实现的,但不利于后期扩展或维护(比如再添加一个持久化组建,则原有的组建可能都需要修改),此时,若添加一个中介者,来协调他们,事儿就好办多了:

数据持久化的接口:

//同事(接口) public interface IPersistent { void getData(Object data); void getData(Object data,Midiator midiator); void saveData(); } 分别实现持久化到文件和持久化到数据库的组件(具体同事):

//具体同事 public class PersistentFile implements IPersistent{ private Object data; @Override public void getData(Object data, Midiator midiator) { getData(data); midiator.notifyOther(this, data); } @Override public void saveData() { System.out.println(data + " 已保存到文件"); } @Override public void getData(Object data) { this.data = data; saveData(); } }

//具体同事 public class PersistentDB implements IPersistent{ private Object data; @Override public void getData(Object data, Midiator midiator) { getData(data); midiator.notifyOther(this, data); } @Override public void saveData() { System.out.println(data + " 已保存到数据库"); } @Override public void getData(Object data) { this.data = data; saveData(); } } 中介者:

//具体中介者 public class Midiator { PersistentDB persistentDB;//此处可以使用List来存放所有的同事 PersistentFile persistentFile; public Midiator setPersistentDB(PersistentDB persistentDB) { this.persistentDB = persistentDB; return this; } public Midiator setPersistentFile(PersistentFile persistentFile) { this.persistentFile = persistentFile; return this; } public void notifyOther(IPersistent persistent,Object data){ if(persistent instanceof PersistentDB)//如果同事都放在List中,此处遍历即可 persistentFile.getData(data); if(persistent instanceof PersistentFile) persistentDB.getData(data); } } 使用:

public class TestUse { public static void main(String args[]){ Object data = “数据”; PersistentDB persistentDB = new PersistentDB(); PersistentFile persistentFile = new PersistentFile(); Midiator midiator = new Midiator(); midiator.setPersistentDB(persistentDB).setPersistentFile(persistentFile); persistentDB.getData(data, midiator); persistentFile.getData(data, midiator); } }//输出(省略了换行符):数据 已保存到数据库数据 已保存到文件数据 已保存到文件数据 已保存到数据库 就上例,如果还有许多的持久化组件(具体同事),可以在中介者中使用一个List来存放他们的引用,set的时候就添加。在通知其他同事时,遍历这个List,除了参数本身这个同事,其他的依次通知,即可实现。

中介者消除了同事与同事间直接的关联。

别名:标记(Another Name:Token)

Without violating encapsulation,captrue and externalize an object' orifianl state so that the object can be restored to this state later.

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存该状态,这样就可以将该对象恢复到之前保存的状态。

何时使用:

必须保存一个对象在某一时刻的全部或部分状态,以便在需要时恢复该对象先前的状态。
一个对象不想通过提供public权限的,诸如getXXX()的方法让其他对象得到自己IDE内部状态。
优点:

备忘录模式使用备忘录可以吧原先者的内部状态全部保存起来,使是有很“亲密”的对象可以访问备忘录中的数据。
备忘录模式强调了类设计单一责任的原则,即将状态的刻画和保存分开。
备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。 备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。

备忘录模式中有三种角色:

备忘录(Memento)角色:将发起人(Originator)对象的内战状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。
发起人(Originator)角色:创建一个含有当前的内部状态的备忘录对象。使用备忘录对象存储其内部状态。
负责人(Caretaker)角色:负责保存备忘录对象。不检查备忘录对象的内容。
先看一个简单的实现方式:

//简单的备忘录模式 public class SimpleMemento { public static void main(String[] args) throws Exception { Originator originator = new Originator(); //发起人,要被保存的对象,也是他创建要保存的信息的 Caretaker caretaker = new Caretaker(); //辅助保存的对象 originator.setState(“stateOne”); //设置状态 caretaker.saveMemento(originator.createMemento()); //保存状态 originator.setState(“stateTwo”); //修改状态 originator.recoverMemento(caretaker.recoverMemento()); //恢复状态 } } //发起人 class Originator { private String state; public Memento createMemento(){ return new Memento(state); } public void recoverMemento(Memento memento){ this.state = memento.getState(); } public String getState() { return state; } public void setState(String state) { this.state = state; } } //备忘录 class Memento { private String state; public Memento(String state){ this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; } } //负责人 class Caretaker { private Memento memento; public Memento recoverMemento() throws Exception{ if(memento==null) throw new Exception(“没有保存的状态”); return this.memento;//恢复状态 } public void saveMemento(Memento memento){ this.memento = memento;//保存状态 } } 备忘录角色对任何对象都提供一个接口,备忘录角色的内部所存储的状态就对所有对象公开,因此是破坏封装性的。

按照定义中的要求,备忘录角色要保持完整的封装。最好的情况便是:备忘录角色只应该暴露操作内部存储属性的的接口给“备忘发起角色”。

如果上例中,我们把备忘录以发起人的私有内部类的方式实现的话,那它就只能被发起人访问了,这正好就符合备忘录模式的要求,但是我们的负责人是需要存放备忘录的引用的,于是,我们提供一个公共的接口,他是空的,我们用备忘录实现它,主要就是利用其中的类型信息,具体实现如下:

//备忘录模式 public class BlackMemento { public static void main(String[] args) { BlankOriginator originator = new BlankOriginator(); //发起人 BlackCaretaker caretaker = new BlackCaretaker(); //负责人 originator.setState(“stateOne”); //设置状态 caretaker.saveMemento(originator.createMemento()); //保存信息 originator.setState(“stateTwo”); //修改状态 originator.recoverMemento(caretaker.recoverMemento());//恢复状态 } } interface MementoIF {} //发起人 class BlankOriginator { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } public MementoIF createMemento(){ return new Memento(state); } public void recoverMemento(MementoIF memento){ this.setState(((Memento)memento).getState()); } //以内部类实现备忘录角色 private class Memento implements MementoIF{ private String state; private Memento(String state){ this.state = state; } private String getState() { return state; } } } //负责人 class BlackCaretaker { private MementoIF memento; public MementoIF recoverMemento(){

标签: 智能型差压变送器mx

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台