23种设计模式——创建型模型

23种设计模式

创建型模型

单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式

结构型模式

适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

行为型模式

模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

单例模式

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

优点:由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决

单例模式有5种常见的实现方法,主要有懒汉式跟饿汉式实现,其他有双重检测锁式、静态内部类式、枚举单例式

饿汉式

线程安全,调用效率高,但是不能延时加载

public class HungerySingleton {
    // 饿汉式非常“饿” 上来就直接new一个对象 不管后面会不会用到
    private static HungerySingleton h = new HungerySingleton();

    // 私有构造器
    private HungerySingleton() {};

    // 供外部拿到该对象
    public static HungerySingleton getInstance() {
        return h;
    }
}

懒汉式

线程安全,调用效率不高,但是可以延时加载

public class LazySingleton {
    // 懒汉式一开始不会初始化,等到用的时候初始化
    private static LazySingleton instance;

    // 私有构造器
    private LazySingleton() {};

    // 需要添加synchronized,方法同步调用效低
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

双重检测锁式

由于JVM底层内部模型元婴,偶尔会出现问题,不建议使用

public class DoubleCheckSingleton {
    private static DoubleCheckSingleton instance = null;

    // 私有化构造器
    private DoubleCheckSingleton() {};

    public static DoubleCheckSingleton getInstance() {
        if (instance == null) {
            DoubleCheckSingleton dc;
            synchronized (DoubleCheckSingleton.class) {
                dc = instance;
                if (dc == null) {
                    synchronized (DoubleCheckSingleton.class) {
                        if (dc == null) {
                            dc = new DoubleCheckSingleton();
                        }
                    }
                    instance = dc;
                }
            }
        }
        return instance;
    }
}

静态内部类式

线程安全,调用效率高,可以延时加载

public class StaticClassSingleton {
    private static class StaticClassSingletonInstance {
        private static final StaticClassSingleton instance = new StaticClassSingleton();
    }

    public static StaticClassSingleton getInstance() {
        return StaticClassSingletonInstance.instance;
    }

    // 私有化构造器
    private StaticClassSingleton() {};
}

枚举单例

线程安全,调用效率高,不能延时加载

public enum EnumSingleton {
    // 这个枚举元素本身就是单例的
    INSTANCE;

    // 添加自己的操作
    public void singletonOperation() {

    }
}

如何选用

  • 当占用资源少,不需要延时加载时,枚举式好于饿汉式
  • 当单例对象占用资源大,需要延时加载时,静态内部类式好于懒汉式
  • 也就是说枚举式替换饿汉式,静态内部类式替换懒汉式

破解单例模式

破解单例模式的方式有两种,反射跟反序列化,但是都不包括破解枚举单例

反射破解

反射可以破解上面列举的单例模式,但是不包含枚举

public static void main(String[] args) throws Exception {
    Class<LazySingleton> cls = (Class<LazySingleton>) Class.forName("com.jinxin.singleton.LazySingleton");

    // 获取构造器
    Constructor<LazySingleton> c = cls.getDeclaredConstructor(null);
    c.setAccessible(true);   // 允许访问私有构造器
    // 创建对象
    LazySingleton l1 = c.newInstance();
}

通过反射对构造函数进行去私有化后就可以创建新的对象

如何防止?

在单例模式的私有构造器中对单例对象进行判断,如果不为空说明已经创建了,直接抛出异常即可

// 私有构造器
private LazySingleton() {
    if (instance != null) {
        throw new RuntimeException();
    }
};

反序列化

反序列化可以破解上面列举的单例模式(不包含枚举)

LazySingleton l1 = LazySingleton.getInstance();
// 序列化
FileOutputStream fos = new FileOutputStream("d:/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(l1);
oos.close();
fos.close();

// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
LazySingleton l2 = (LazySingleton) ois.readObject();
System.out.println(l1);
System.out.println(l2);

通过反序列化拿到的对象l2l1已经不是同一个对象了,当然序列化的前提是LazySingleton需要实现Serializable接口

如何防止?

防止措施需要使用到反序列化的特性,如果在被创建的对象的类中定义了readResolve()方法,反序列化会直接返回该方法返回的对象,而不会新创建一个对象

如只需要在LazySingleton中加入下面的代码即可

// 反序列化时,直接调用该方法返回旧的对象,而不需要创建新的对象
private Object readResolve() throws ObjectStreamException {
    return instance;
}  

工厂模式

工厂模式的作用是实例化对象,用工厂方法替代new操作,实现创建者和调用者的分离

工厂模式分类

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

简单工厂模式

简单工厂模式是使用最多的一种模式,它用来生产同一等级结构中的任意产品(对于新增的产品,需要修改已有代码)

首先准备好两个类

一个Car接口跟两个实现类AudiByd

public interface Car {
     void run();
}

  

public class Audi implements Car {
    public void run() {
        System.out.println("奥迪再跑!");
    }
}

  

public class Byd implements Car {
    public void run() {
        System.out.println("比亚迪再跑");
    }
}

  

如果需要在客户端使用

public class Client1 {
    public static void main(String[] args) {
        Car c1 = new Audi();
        Car c2 = new Byd();

        c1.run();
        c2.run();
    }
}

问题出来了,现在客户端需要同时依赖Car、Audi以及Byd。

那么当使用工厂模式后,就可以将这些依赖全部交给工厂类管理

public class CarFactory {

    public static Car createCar(String type) {
        if ("奥迪".equals(type)) {
            return new Audi();
        } else if ("比亚迪".equals(type)) {
            return new Byd();
        } else {
            return null;
        }
    }
}

或者

public class CarFactory {

    public static Car createAudi() {
        return new Audi();
    }

    public static Car createByd() {
        return new Byd();
    }
}

现在在客户端使用

public class Client1 {
    public static void main(String[] args) {
        Car c1 = CarFactory.createCar("奥迪");
        Car c2 = CarFactory.createCar("比亚迪");

        c1.run();
        c2.run();
    }
}

总结

简单工厂模式也叫静态工厂模式,因为工厂类一般使用的都是静态方法。

简单工厂对于增加新的产品无能为力,当然这种无能为力是指遵循开闭原则不修改代码,如果非要修改也是可以增加新的产品的

工厂方法模式

用来生产同一等级结构中的固定产品(支持增加任意产品)

工厂方法模式的出现是为了避免简单的工厂模式的缺点——不完全满足OCP。

工厂方法模式会有一个总的工厂接口,然后为每一个类创建一个工厂

public interface CarFactory {
    Car createCar();
}

  

public class AudiFactory implements CarFactory {
    
    @Override
    public Car createCar() {
        return new Audi();
    }
}

  

public class BydFactory implements CarFactory {
    
    @Override
    public Car createCar() {
        return new Byd();
    }
}  

总结

工厂方法模式无论从设计复杂度还是管理难度上面都要劣于简单工厂模式,因此如果根据设计理论建议:使用工厂方法模式。但实际上:建议使用简单工厂模式

抽象工厂模式

用来生产不同产品族的全部产品(对于新增加的产品,无能为力;支持增加产品族)

前面讲的都是只有一个接口时的处理方式,但是如果有多个接口,这时无论是简单工厂模式还是工厂方法模式都已经无能为力了,这时需要通过抽象工厂模式解决

首先定义三个接口,分别为他们编写两个实现类,这里以汽车举例,分别定义引擎、座位、轮胎三个接口,实现类是奢侈轮胎跟差轮胎,座位引擎毅然

package com.jinxin.factory.abstractfactory;

public interface Engine {
    void run();

    void start();
}

class LuxuryEngine implements Engine {
    @Override
    public void run() {
        System.out.println("转得快");
    }

    @Override
    public void start() {
        System.out.println("启动快!可以自动启停!");
    }
}

class LowEngine implements Engine {
    @Override
    public void run() {
        System.out.println("转得慢");
    }

    @Override
    public void start() {
        System.out.println("启动慢!不可以自动启停!");
    }
}

  

package com.jinxin.factory.abstractfactory;

public interface Seat {
    void massage();
}

class LuxurySeat implements Seat {
    @Override
    public void massage() {
        System.out.println("可以自动按摩!");
    }
} 

class LowSeat implements Seat {
    @Override
    public void massage() {
        System.out.println("不可以自动按摩!");
    }
} 

  

package com.jinxin.factory.abstractfactory;

public interface Tyre {
    void revolve();
}

class LuxuryTyre implements Tyre {
    @Override
    public void revolve() {
        System.out.println("旋转不磨损");
    }
}

class LowTyre implements Tyre {
    @Override
    public void revolve() {
        System.out.println("磨损快");
    }
}

然后定义一个车的工厂接口

package com.jinxin.factory.abstractfactory;

public interface CarFactory {
    Engine createEngine();
    Seat createSeat();
    Tyre createTyre();
}

定义奢侈车跟低端车的接口

package com.jinxin.factory.abstractfactory;

public class LuxuryCarFactory implements CarFactory {
    @Override
    public Engine createEngine() {
        return new LuxuryEngine();
    }

    @Override
    public Seat createSeat() {
        return new LuxurySeat();
    }

    @Override
    public Tyre createTyre() {
        return new LuxuryTyre();
    }
}

  

package com.jinxin.factory.abstractfactory;

public class LowCarFactory implements CarFactory {
    @Override
    public Engine createEngine() {
        return new LowEngine();
    }

    @Override
    public Seat createSeat() {
        return new LowSeat();
    }

    @Override
    public Tyre createTyre() {
        return new LowTyre();
    }
}

客户端只需要通过调用LuxuryCarFactoryLowCarFactory即可

建造者模式

建造者模式的本质是分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。从而可以构造出复杂的对象,这个模式适用于某个对象的构建过程复杂的情况

以构建火箭为例子,假设火箭由发动机、轨道舱、逃逸塔构成,首先我们需要构建飞船类AirShip以及发动机、逃逸塔等类

public class AirShip {
    private OrbitalModule orbitalModule;   // 轨道舱
    private Engine engine;   // 发动机
    private EscapeTower escapeTower;  // 逃逸塔

    public void setOrbitalModule(OrbitalModule orbitalModule) {
        this.orbitalModule = orbitalModule;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void setEscapeTower(EscapeTower escapeTower) {
        this.escapeTower = escapeTower;
    }

    public OrbitalModule getOrbitalModule() {
        return this.orbitalModule;
    }

    public Engine getEngine() {
        return this.engine;
    }

    public EscapeTower getEscapeTower() {
        return this.escapeTower;
    }
}

/**
 * 轨道舱类
 */
class OrbitalModule {
    private String name;

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

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

/**
 * 发动机类
 */
class Engine {
    private String name;

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

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

/**
 * 逃逸塔类
 */
class EscapeTower {
    private String name;

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

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

然后构建飞船的Builder类,下面是接口以及实现类

public interface AirShipBuilder {
    Engine createEngine();
    OrbitalModule createOrbitalModule();
    EscapeTower createEscapeTower();
}

  

public class JxAirShipBuilder implements AirShipBuilder {
    @Override
    public Engine createEngine() {
        System.out.println("构建发动机");
        return new Engine("金牌发动机");
    }

    @Override
    public OrbitalModule createOrbitalModule() {
        System.out.println("构建轨道舱");
        return new OrbitalModule("金牌轨道舱");
    }

    @Override
    public EscapeTower createEscapeTower() {
        System.out.println("构建逃逸塔");
        return new EscapeTower("金牌逃逸塔");
    }
}

再就是构建飞船的Director类,下面是接口以及实现类

public interface AirShipDirector {
    // 组装飞船对象
    AirShip directorAirShip();
}

  

public class JxAirShipDirector implements AirShipDirector {
    private AirShipBuilder builder;

    public JxAirShipDirector(AirShipBuilder builder) {
        this.builder = builder;
    }

    @Override
    public AirShip directorAirShip() {
        Engine e = builder.createEngine();
        OrbitalModule orbitalModule = builder.createOrbitalModule();
        EscapeTower escapeTower = builder.createEscapeTower();
        // 组装成飞船
        AirShip airShip = new AirShip();
        airShip.setEngine(e);
        airShip.setOrbitalModule(orbitalModule);
        airShip.setEscapeTower(escapeTower);
        return airShip;
    }
}

现在客户端使用

public static void main(String[] args) {
    AirShipDirector airShipDirector = new JxAirShipDirector(new JxAirShipBuilder());
    AirShip airship = airShipDirector.directorAirShip();
    System.out.println(airship.getEngine());
}

UML关系图

开发中应用场景

什么时候使用建造者模式?当被构建对象的创建十分复杂,有各种组件,就可以使用建造者模式

应用场景

  • StringBuilder类的append方法
  • SQL中的PreparedStatement

  • JDOM中,DomBuilder、SAXBuilder

原型模式

通过new产生一个对象需要非常繁琐的数据准备或者访问权限,则可以使用原型模式,这其实就是java中的克隆技术,以某个对象为原型,复制出新的对象,新的对象就具备原型对象的特点,这样的优点就是效率高。

克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同,并且克隆出的新对象不会影响原型对象

原型模式的实现

  • 实现Cloneable接口和clone方法
  • Prototype模式中实现起来最困难的地方就是内存复制操作,不过在Java中提供了clone()方法

其实实现原型模式只需要给要实现原型模式的对象实现Cloneable接口,然后重写Object类中的clone方法即可

import java.util.Date;

public class Sheep implements Cloneable {
    private String name;
    private Date birthday;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();   // 直接调用Object对象的clone()方法
    }



    
    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param birthday the birthday to set
     */
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    /**
     * @return the birthday
     */
    public Date getBirthday() {
        return birthday;
    }

    public Sheep(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }
}

使用

public static void main(String[] args) throws Exception {
    Sheep sheep = new Sheep("多利", new Date(12312312243L));
    System.out.println(sheep);

    Sheep s = (Sheep) sheep.clone();
    System.out.println(s);
}

但是此时有一个问题,就是这只是一个浅克隆,复制的对象和原型对象公用一个公共的时间对象,如果时间对象发生修改,两个对象的时间对象都会发生改变

那么现在想将时间对象也复制出来就应该使用深复制,在深复制的clone方法中只需要将属性也进行复制即可

@Override
public Object clone() throws CloneNotSupportedException {
    Object obj = super.clone();
    // 添加如下代码实现深复制
    Sheep s = (Sheep) obj;
    s.setBirthday((Date) this.birthday.clone());   // 把属性也进行克隆
    return obj;
}

使用序列化和反序列化实现深克隆

public static void main(String[] args) throws Exception {
    Sheep s = new Sheep("name", new Date())

    // 序列化
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(oos);
    byte[] bytes = bos.toByteArray();
    // 反序列化
    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
    ObjectInputStream ois = new ObjectInputStream(bis);
    Sheep s1 = (Sheep) ois.readObject();   // 生成的新对象
}

这就是23种设计模式中所有的创建型模式

 

posted @ 2019-10-17 15:49  Jin同学  阅读(392)  评论(0编辑  收藏  举报