Design Pattern

“Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice” [AIS+77]. —— Christopher Alexander

一般来说,模式有四个基本要素:

模式名称 是一个句柄,我们可以用一两个词来描述设计问题、它的解决方案和后果。

问题 描述了何时应用模式。

解决方案 描述了构成设计的元素、它们之间的关系、职责和协作。

后果 是应用该模式的结果和权衡。

Singleton

意图

确保一个类只有一个实例,并提供一个全局访问点

动机

我们如何确保一个类只有一个实例并且该实例易于访问?

更好的解决方案是让类本身负责跟踪它的唯一实例。 该类可以确保不能创建其他实例(通过拦截创建新对象的请求), 它可以提供一种访问实例的方法。 这就是单例模式。

适用性

  • 一个类必须只有一个实例,并且客户端必须可以从众所周知的访问点访问它。

  • 当唯一实例应该可以通过子类化进行扩展时,客户应该能够在不修改代码的情况下使用扩展实例。

结构

单例

后果

单例模式有几个好处:

  • 对唯一实例的控制访问。

  • 减少名称空间。

  • 允许改进操作和表示。

  • 允许可变数量的实例。

  • 比类操作更灵活。

实现

singleton zh_CN singleton2 zh_CN

lazy loading

  • thread not safe
package com.ryo.singleton;

/**
 * Created by 侯彬彬 on 2016/7/15.
 */
public class Lazy {
    private Lazy(){}

    private static Lazy lazy = null;

    public static Lazy instance() {
        if(lazy == null) {
            lazy = new Lazy();
        }

        return lazy;
    }
}
  • thread safe

It’s pity this way is inefficiency.

public class LazyThreadSafe {
    private LazyThreadSafe(){}

    private static LazyThreadSafe lazy = null;

    public static synchronized LazyThreadSafe instance() {
        if(lazy == null) {
            lazy = new LazyThreadSafe();
        }

        return lazy;
    }
}

Best practice: a good way to solve it. (Lazy loading and thread safe)

package com.ryo.singleton;

public class Singleton {
    private Singleton(){}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static  Singleton instance() {
       return SingletonHolder.INSTANCE;
    }
}

starve

Thread safe, not lazy loading. Usually, we can use this…

package com.ryo.singleton;

public class Starve {
    private Starve(){}
    private static Starve starve = new Starve();

    public static Starve instance() {
        return starve;
    }
}

enum

The best way to realize singleton is to use enum

enum

Prototype

使用原型实例指定要创建的对象种类,并通过复制此原型来创建新对象。

zh_CN原型

适用性

当系统应该独立于其产品的创建、组合和表示方式时,使用原型模式; 和

  • 当要实例化的类在运行时指定时,例如,通过动态加载

  • 避免构建与产品类层次结构平行的工厂类层次结构

  • 当一个类的实例可以具有仅有的几种不同状态组合中的一种时。

安装相应数量的原型并克隆它们可能更方便,而不是每次都使用适当的状态手动实例化类。

后果

它向客户端隐藏了具体的产品类别,从而减少了客户端知道的名称数量。

此外,这些模式让客户端无需修改即可使用特定于应用程序的类。

Implementation

  • Prototype.java
package com.ryo.prototype;

/**
 * Created by 侯彬彬 on 2016/7/15.
 */
public class Prototype implements Cloneable {
    private String name;

    public Prototype(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Prototype{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • PrototypeTest.java
public class PrototypeTest extends TestCase {
    @Test
    public void testClone() throws Exception {
        Prototype prototype = new Prototype("ryo");
        final String json = "Prototype{name='ryo'}";

        assertEquals(json, prototype.toString());

        Prototype clone = (Prototype) prototype.clone();
        assertEquals(json, clone.toString());
    }
}

shallow copy

shallowCopy

Java object’s clone() is shallow copy. See the flowing demo.

  • Person.java
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • Prototype.java
public class Prototype implements Cloneable {
    private Person person;

    public Prototype(Person person) {
        this.person = person;
    }

    public Person getPerson() {
        return person;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Prototype{" +
                "person=" + person +
                '}';
    }
}
  • test
public class PrototypeTest extends TestCase {
    @Test
    public void testClone() throws Exception {
        Person person = new Person("ryo", 23);
        Prototype prototype = new Prototype(person);

        Prototype clone = (Prototype) prototype.clone();
        Person person1 = clone.getPerson();
        person1.setName("jack");

        assertEquals("Prototype{person=Person{name='jack', age=23}}", prototype.toString());
    }
}

deep copy

shallowCopy

  • add Serializable for Person
public class Person implements Serializable {
    //...
}
  • DeepCopy.java
package com.ryo.prototype;

import java.io.*;

/**
 * Created by 侯彬彬 on 2016/7/15.
 */
public class DeepCopy implements Cloneable, Serializable {
    private Person person;

    public DeepCopy(Person person) {
        this.person = person;
    }

    public Person getPerson() {
        return person;
    }

    @Override
    public String toString() {
        return "DeepCopy{" +
                "person=" + person +
                '}';
    }

    public Object deepClone() {
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        Object object = null;
        try {
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(this);
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
            ObjectInputStream oi = new ObjectInputStream(bi);
            object = oi.readObject();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }

        return object;
    }
}
  • DeepCopyTest.java
public class DeepCopyTest extends TestCase {

    @Test
    public void testDeepClone() throws Exception {
        Person person = new Person("ryo", 23);
        DeepCopy deepCopy = new DeepCopy(person);

        DeepCopy clone = (DeepCopy) deepCopy.deepClone();
        Person person1 = clone.getPerson();
        person1.setName("jack");

        assertEquals("DeepCopy{person=Person{name='jack', age=23}}", clone.toString());
        assertEquals("DeepCopy{person=Person{name='ryo', age=23}}", deepCopy.toString());
    }
}

Factory

simple factory

simpleFactory

  • interface
public interface Car {
    String info();
}
  • class1
public class BaomaCar implements Car {
    public static final String BAO_MA = "Baoma";

    @Override
    public String info() {
        return BAO_MA;
    }
}
  • class2
public class AodiCar implements Car {
    public static final String AO_DI = "Ao di";

    @Override
    public String info() {
        return AO_DI;
    }
}
  • factory
public class SimpleFactory {
    private SimpleFactory(){}

    public static Car factory(String type) {
        Car car = null;

        if (type.equals(BaomaCar.BAO_MA)) {
            car = new BaomaCar();
        } else if (type.equals(AodiCar.AO_DI)) {
            car = new AodiCar();
        }

        return car;
    }
}
  • test
@Test
public void testFactory() {
    Car car = SimpleFactory.factory(BaomaCar.BAO_MA);
    assertEquals(BaomaCar.BAO_MA, car.info());

    Car car1 = SimpleFactory.factory(AodiCar.AO_DI);
    assertEquals(AodiCar.AO_DI, car1.info());
}

factory method

simpleFactory

  • Interface
public interface Movable {
    void run();
}
  • class1
public class AirMovable implements Movable {
    @Override
    public void run() {
        System.out.println("air running...");
    }
}
  • class2
public class SeaMovable implements Movable {
    @Override
    public void run() {
        System.out.println("Sea running...");
    }
}
  • InterfaceFactory
public abstract class AbstractMoveFactory {
    abstract Movable getInstance();
}
  • class1Factory
public class SeaMoveFactory extends AbstractMoveFactory {
    @Override
    Movable getInstance() {
        return new SeaMovable();
    }
}
  • class2Factory
public class AirMoveFactory extends AbstractMoveFactory {
    @Override
    Movable getInstance() {
        return new AirMovable();
    }
}
  • test
public class FactoryMethodTest extends TestCase {
    @Test
    public void testGetInstance() {
        AbstractMoveFactory abstractMoveFactory = new AirMoveFactory();
        Movable movable = abstractMoveFactory.getInstance();
        movable.run();
    }

    @Test
    public void testGetInstance2() {
        AbstractMoveFactory abstractMoveFactory = new SeaMoveFactory();
        Movable movable = abstractMoveFactory.getInstance();
        movable.run();
    }
}
  • result
Sea running...
air running...

Process finished with exit code 0

abstract factory

Intent

提供一个接口,用于创建相关或依赖对象的系列,而无需指定它们的具体类。

适用性

  • 系统应该独立于其产品的创建、组合和表示方式。

  • 系统应配置多个产品系列之一。

  • 一系列相关的产品对象旨在一起使用,您需要强制执行此约束。

  • 你想提供一个产品的类库,你只想展示它们的接口,而不是它们的实现。

Structure

abstractFactory

Consequences

  • 它隔离具体类。

  • 它使产品系列的交换变得容易。

  • 它促进了产品之间的一致性。

  • 支持新产品很困难

Implementation

  • AbstractFactory.java
public abstract class AbstractFactory {
    public abstract Vehicle createVehicle();
    public abstract Fruit createFruit();
}
  • DefaultFactory.java
public class DefaultFactory extends AbstractFactory {
    @Override
    public Vehicle createVehicle() {
        return new Boat();
    }

    @Override
    public Fruit createFruit() {
        return new Apple();
    }
}
  • Vehicle and Boat
public interface Vehicle {
    void info();
}

public class Boat implements Vehicle {
    @Override
    public void info() {
        System.out.println("Vehicle boat...");
    }
}
  • Fruit & Apple
public interface Fruit {
    void info();
}

public class Apple implements Fruit {
    @Override
    public void info() {
        System.out.println("Fruit Apple...");
    }
}
  • test
public class AbstractFactoryTest extends TestCase {
    @Test
    public void testCreateVehicle() throws Exception {
        DefaultFactory defaultFactory = new DefaultFactory();
        Vehicle vehicle = defaultFactory.createVehicle();
        vehicle.info();
    }


    @Test
    public void testCreateFruit() throws Exception {
        DefaultFactory defaultFactory = new DefaultFactory();
        Fruit fruit = defaultFactory.createFruit();
        fruit.info();
    }
}
  • result
Fruit Apple...
Vehicle boat...

Process finished with exit code 0