设计模式-创建型模式

文章目录

  • 一、单例模式
    • 1.饿汉式
      • (1) 静态变量
      • (2) 静态代码块
      • (3) 枚举方式
    • 2.懒汉式
      • (1) 双检锁
      • (2) 静态内部类
    • 3.破坏单例模式
      • (1) 序列化
      • (2) 反射
    • 4.解决单例模式被破坏
      • (1) 序列化、反序列化破坏单例模式的解决方法
      • (2) 反射破坏单例解决
  • 二、工厂方法模式
    • 1.简单工厂模式
    • 2.工厂方法模式
  • 三、抽象工厂模式
  • 四、原型模式
  • 五、建造者模式

一、单例模式

涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建

提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化对象

1.饿汉式

类加载时就会导致该单例对象被创建

会造成内存的浪费

(1) 静态变量

public class Singleton {

    //私有化构造方法  防止外界通过构造方法创建对象
    private Singleton(){}

    //创建对象
    private static Singleton instance = new Singleton();

    //提供获取对象的公共方法 采用static修饰是为了可以通过类名访问这个方法
    public static Singleton getInstance(){
        return instance;
    }
}

public class Test {

    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        //判断两个对象是否相同
        System.out.println(singleton == singleton1);
    }

}

(2) 静态代码块

public class Singleton {

    private Singleton(){}

    //声明Singleton类型的变量
    private static Singleton instance;

    //在静态代码块中进行赋值
    static {
        instance = new Singleton();
    }

    //对外提供对象的获取方法
    public static Singleton getInstance(){
        return instance;
    }

}

public class Test {

    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        //判断是否为同一个对象
        System.out.println(instance == instance1);
    }

}

(3) 枚举方式

极力推荐

因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,
枚举的写法非常简单,而且枚举类型是所有单例实现中唯一一种不会被破坏的单例实现模式
public enum Singleton {

    INSTANCE

}

2.懒汉式

类加载时不会导致该单例对象被创建,而是在首次使用该对象时才会创建

(1) 双检锁

/**
 * 双检锁
 */
public class Singleton {

    //私有化构造方法 防止外界创建对象
    private Singleton(){}
    
    /**
     * volatile是为了防止返回未完全创建的对象 
     * 因为对象的创建在汇编阶段分为三步,防止一个线程返回另一个线程未完全创建的对象
     */
    private static volatile Singleton instance;

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

(2) 静态内部类

实例对象由内部类创建,由于 JVM 在加载外部类的过程中是不会加载静态内部类的,
只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被static修饰,
保证只被实例化一次,并且严格按照实例化顺序
public class Singleton {

    private Singleton(){}
    
    //定义一个静态内部类
    private static class SingletonHolder {
        //在内部类中声明并初始化外部类的对象
        private static final Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }

}

3.破坏单例模式

使上面定义的单例类可以创建多个对象,枚举方式的不能被破坏

(1) 序列化

public class Singleton implements Serializable {

    private Singleton(){}

    //定义一个静态内部类
    private static class SingletonHolder {
        //在内部类中声明并初始化外部类的对象
        private static final Singleton instance = new Singleton();
    }

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

}

/**
 * 测试序列化与反序列化破坏单例模式
 */
public class Test {

    public static void main(String[] args) throws Exception {
        //writeObjectToFile();
        //通过控制台打印可以发现两次的对象不是同一个对象
        readObjectFromFile();
        readObjectFromFile();
    }

    //从文件中读取数据(对象)
    public static void readObjectFromFile() throws Exception {
        //创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("a.txt")));
        //读取对象
        Singleton instance = (Singleton) ois.readObject();
        System.out.println(instance);
        //释放资源
        ois.close();
    }

    //向文件中写数据(对象)
    public static void writeObjectToFile() throws Exception {
        //获取Singleton对象
        Singleton instance = Singleton.getInstance();
        //创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("a.txt")));
        //写对象
        oos.writeObject(instance);
        //释放资源
        oos.close();
    }

}

(2) 反射

public class Singleton {

    private Singleton(){}

    //定义一个静态内部类
    private static class SingletonHolder {
        //在内部类中声明并初始化外部类的对象
        private static final Singleton instance = new Singleton();
    }

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

}


/**
 * 测试反射破坏单例模式
 */
public class Test {

    public static void main(String[] args) throws Exception {
        //获取Singleton的字节码对象
        Class clazz = Singleton.class;
        //获取无参构造方法对象
        Constructor constructor = clazz.getDeclaredConstructor();
        //取消访问检查
        constructor.setAccessible(true);
        //创建Singleton对象
        Singleton instance = (Singleton) constructor.newInstance();
        Singleton instanc1 = (Singleton) constructor.newInstance();
        System.out.println(instance == instanc1);//false 被破坏了
    }

}

4.解决单例模式被破坏

(1) 序列化、反序列化破坏单例模式的解决方法

在Singleton类中添加 readResolve() 方法,在反序列化时被反射调用,如果定义了这个方法,
就返回这个方法的值,如果没有定义,则返回新new出来的对象
public class Singleton implements Serializable {

    private Singleton(){}

    //定义一个静态内部类
    private static class SingletonHolder {
        //在内部类中声明并初始化外部类的对象
        private static final Singleton instance = new Singleton();
    }

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

    /**
     * 当进行反序列化时会自动调用该方法,将该方法的返回值直接返回
     */
    public Object readResolve(){
        return SingletonHolder.instance;
    }

}


/**
 * 测试解决序列化与反序列化破坏单例模式
 */
public class Test {

    public static void main(String[] args) throws Exception {
        //writeObjectToFile();
        readObjectFromFile();
        readObjectFromFile();
    }

    //从文件中读取数据(对象)
    public static void readObjectFromFile() throws Exception {
        //创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("a.txt")));
        //读取对象
        Singleton instance = (Singleton) ois.readObject();
        System.out.println(instance);
        //释放资源
        ois.close();
    }

    //向文件中写数据(对象)
    public static void writeObjectToFile() throws Exception {
        //获取Singleton对象
        Singleton instance = Singleton.getInstance();
        //创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("a.txt")));
        //写对象
        oos.writeObject(instance);
        //释放资源
        oos.close();
    }

}

(2) 反射破坏单例解决

public class Singleton {

    private static boolean flag = false;

    //私有构造方法
    private Singleton(){
        //防止多线程下出现错误
        synchronized (Singleton.class) {
            //判断flag的值是否为true,如果为true则说明这不是第一次访问 直接抛出异常
            if (flag){
                throw new RuntimeException("不能创建多个对象");
            }
            //将flag的值设置为true
            flag = true;
        }
    }

    //定义一个静态内部类
    private static class SingletonHolder {
        //在内部类中声明并初始化外部类的对象
        private static final Singleton instance = new Singleton();
    }

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

}

二、工厂方法模式

1.简单工厂模式

结构:
    抽象产品:定义了产品的规范,描述了产品的主要特性和功能
    具体产品:实现或继承抽象产品的子类
    具体工厂:提供了创建产品的方法,调用者通过该方法来获取产品
    
优点:
    封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,
    这样以后就避免了修改客户端代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,
    这样就降低了客户代码修改的可能性,更加容易扩展。
    在多个客户端的情况下好处会很明显,不用修改每个客户端,只需要修改工厂

缺点:
    增加新产品时还是需要修改工厂类的代码,违背了开闭原则
public abstract class Coffee {

    public abstract String getName();

    //加糖
    public void addSugar(){
        System.out.println("加糖");
    }

    //加茅台
    public void addWine(){
        System.out.println("加茅台");
    }
}

/**
 * 酱香咖啡
 */
public class JiangCoffee extends Coffee{

    @Override
    public String getName() {
        return "酱香拿铁";
    }
}

/**
 * 飞天咖啡
 */
public class FlyCoffee extends Coffee {

    @Override
    public String getName() {
        return "飞天咖啡";
    }
}

/**
 * 简单工厂
 */
public class SimpleFactory {

    public Coffee orderCoffee(String type){
        //声明coffee类型的变量,根据不同type创建对象
        Coffee coffee = null;
        if (type.equals("酱香拿铁")){
            coffee = new JiangCoffee();
        } else if (type.equals("飞天咖啡")){
            coffee = new FlyCoffee();
        } else {
            throw new RuntimeException("没有这种咖啡");
        }
        return coffee;
    }

}

public class CoffeeStore {

    public Coffee orderCoffee(String type){
        SimpleFactory factory = new SimpleFactory();
        //生产咖啡
        Coffee coffee = factory.orderCoffee(type);
        //加配料
        coffee.addSugar();
        coffee.addWine();
        return coffee;
    }

}

2.工厂方法模式

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其
工厂的子类

结构:
    抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
    具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建
    抽象产品:定义了产品的规范,描述了产品的主要特性和功能
    具体产品:实现了抽象产品所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应
    
优点:
    a.用户只需要知道具体工厂的名称就可以得到所要的产品,无需知道产品的具体创建过程
    b.在系统增加新的产品时只需要添加具体的产品类和对应的具体工厂类,无须对原工厂进行任何修改,
    满足开闭原则

缺点:
    每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,增加了系统的复杂度
/**
 * 抽象工厂
 */
public interface CoffeeFactory {

    //创建咖啡
    Coffee createCoffee();

}

/**
 * 具体工厂
 */
public class JiangCoffeeFactory implements CoffeeFactory{

    @Override
    public Coffee createCoffee() {
        return new JiangCoffee();
    }
}

/**
 * 具体工厂
 */
public class FlyCoffeeFactory implements CoffeeFactory{

    @Override
    public Coffee createCoffee() {
        return new FlyCoffee();
    }
}

public abstract class Coffee {

    public abstract String getName();

    //加糖
    public void addSugar(){
        System.out.println("加糖");
    }

    //加茅台
    public void addWine(){
        System.out.println("加茅台");
    }
}

/**
 * 飞天咖啡
 */
public class FlyCoffee extends Coffee {

    @Override
    public String getName() {
        return "飞天咖啡";
    }
}

/**
 * 酱香咖啡
 */
public class JiangCoffee extends Coffee {

    @Override
    public String getName() {
        return "酱香拿铁";
    }
}

public class CoffeeStore {

    private CoffeeFactory factory;

    public void setFactory(CoffeeFactory factory){
        this.factory = factory;
    }

    //点咖啡
    public Coffee orderCoffee(){
        Coffee coffee = factory.createCoffee();
        coffee.addSugar();
        coffee.addWine();
        return coffee;
    }

}

public class Test {

    public static void main(String[] args) {
        //创建咖啡店对象
        CoffeeStore store = new CoffeeStore();
        //创建工厂对象
        CoffeeFactory coffeeFactory = new JiangCoffeeFactory();
        store.setFactory(coffeeFactory);
        Coffee coffee = store.orderCoffee();
        System.out.println(coffee.getName());
    }

}

三、抽象工厂模式

工厂方法模式考虑的是一类产品的生产,只生产同类产品。

抽象工厂模式考虑从多等级产品的生产,将同一个具体工厂所生产的位于不同级别的一组产品称为一个产品族。

是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所有产品的具体类就能得到
同族的不同等级的产品的模式结构

结构:
    抽象工厂:提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品
    具体工厂:主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建
    抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品
    具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,和具体工厂之间是多对一的关系
    
优点:
    当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族的对象
    
缺点:
    当产品组中需要增加一个新的产品时,所有的工厂类都需要进行修改
public abstract class Coffee {

    public abstract String getName();

    //加糖
    public void addSugar(){
        System.out.println("加糖");
    }

    //加茅台
    public void addWine(){
        System.out.println("加茅台");
    }
}

/**
 * 飞天咖啡
 */
public class FlyCoffee extends Coffee {


    @Override
    public String getName() {
        return "飞天咖啡";
    }
}

/**
 * 酱香咖啡
 */
public class JiangCoffee extends Coffee {

    @Override
    public String getName() {
        return "酱香拿铁";
    }
}

/**
 * 甜品抽象类
 */
public abstract class Dessert {

    public abstract void show();

}

public class JiangChocolate extends Dessert{

    @Override
    public void show() {
        System.out.println("酱香巧克力");
    }
}

public class FlyChocolate extends Dessert{

    @Override
    public void show() {
        System.out.println("飞天巧克力");
    }
}

/**
 * 抽象工厂
 */
public interface DessertFactory {

    //生产咖啡
    Coffee createCoffee();

    //生产甜品
    Dessert createDessert();

}

/**
 * 酱香工厂
 */
public class JiangFactory implements DessertFactory{

    @Override
    public Coffee createCoffee() {
        return new JiangCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new JiangChocolate();
    }
}

/**
 * 飞天工厂
 */
public class FlyFactory implements DessertFactory{

    @Override
    public Coffee createCoffee() {
        return new FlyCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new FlyChocolate();
    }
}

public class Client {

    public static void main(String[] args) {
        //创建酱香工厂
        JiangFactory factory = new JiangFactory();
        Coffee coffee = factory.createCoffee();
        Dessert dessert = factory.createDessert();
        System.out.println(coffee.getName());
        dessert.show();
    }

}

四、原型模式

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象

结构:
    抽象原型类:规定了具体原型对象必须实现的 clone() 方法
    具体原型类:实现抽象圆形类的 clone() 方法,它是可被复制的对象
    访问类:使用具体原型类中的 clone() 方法来复制新的对象

克隆:
    浅克隆:创建一个新对象,新对象的属性和原来对象属性完全相同,对于引用数据类型,
           仍指向原有属性所指向的对象的内存地址
    深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象的地址。
           采用序列化和反序列化来深克隆
    
使用场景:
       对象的创建非常复杂,可以使用原型模式快捷的创建对象
       性能和安全要求比较高的时候
public class Citation implements Cloneable{

    private String name;//学生姓名

    public String getName() {
        return name;
    }

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

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }

    public void show(){
        System.out.println(name + " 同学 : 获得奖状");
    }
}


public class CitationTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象
        Citation citation = new Citation();
        //克隆对象
        Citation clone = citation.clone();

        citation.setName("小白");
        clone.setName("小黑");

        citation.show();
        clone.show();
    }

}

深克隆

public class Citation implements Cloneable, Serializable {

    private Student student;

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }

    public void show(){
        System.out.println(student.getName() + " 同学 : 获得奖状");
    }
}

public class Student implements Serializable {

    private String name;

    public String getName() {
        return name;
    }

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

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

public class Test {

    public static void main(String[] args) throws Exception {
        //原型对象
        Citation citation = new Citation();
        Student student = new Student();
        student.setName("小白");
        citation.setStudent(student);

        //克隆对象 采取深克隆
        //创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("a.txt")));
        //写对象
        oos.writeObject(citation);
        //释放资源
        oos.close();
        //创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("a.txt")));
        //读取对象
        Citation citation1 = (Citation) ois.readObject();
        //释放资源
        ois.close();
        citation1.getStudent().setName("小黑");

        citation.show();
        citation1.show();
    }

}

五、建造者模式

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

适用于某个对象的构建过程复杂的情况

实现了构建和装配的解耦。不同的构建器、相同的装配,可以做出不同的对象;相同的构建器、
不同的装配顺序也可以做出不同的对象。

结构:
    抽象建造者类(builder):规定要实现复杂对象的哪些部分的创建,并不涉及具体的部件对象的创建
    具体建造者类:实现 builder ,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,
                提供产品的实例
    产品类:要创建的复杂对象
    指挥者类:调用具体建造者来创建复杂对象的各个部分,在指挥者中不涉及具体的产品信息,
            只负责保证对象各部分完整创建或按某种顺序创建
    
优点:
    a.封装性很好。使用建造者模式可以很好的封装变化,在使用建造者模式的场景中,
      一般产品类和建造者类是比较稳定的,因此将主要的业务逻辑封装在指挥者类中对整体而言可以取得
      比较好的稳定性
    b.产品本身与产品的创建过程解耦
    c.可以更加精细的控制产品的创建过程。
    d.很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成
    
缺点:
    建造者模式所创建的产品一般具有较多的共同点,其组成部分相似。如果产品之间的差异性很大,
    则不适合使用建造者模式
/**
 * 车座
 */
public class Seat {

    private String name;//材质

    public String getName() {
        return name;
    }

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

/**
 * 车架
 */
public class Frame {

    private String name;//材质

    public String getName() {
        return name;
    }

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

/**
 * 自行车产品对象
 */
public class Bike {

    private Seat seat;//车座

    private Frame frame;//车架

    public Seat getSeat() {
        return seat;
    }

    public void setSeat(Seat seat) {
        this.seat = seat;
    }

    public Frame getFrame() {
        return frame;
    }

    public void setFrame(Frame frame) {
        this.frame = frame;
    }
}

public abstract class Builder {

    protected Bike bike = new Bike();
    protected Seat seat = new Seat();
    protected Frame frame = new Frame();

    public abstract void builderFrame();

    public abstract void builderSeat();

    public abstract Bike createBike();
}

/**
 * 具体构建者:构建飞鸽牌自行车
 */
public class FeiGeBuilder extends Builder{

    @Override
    public void builderFrame() {
        frame.setName("碳纤维车架");
        bike.setFrame(frame);
    }

    @Override
    public void builderSeat() {
        seat.setName("真皮车座");
        bike.setSeat(seat);
    }

    @Override
    public Bike createBike() {
        return bike;
    }
}

/**
 * 具体构建者:构建二八大杠自行车
 */
public class GangBuilder extends Builder{

    @Override
    public void builderFrame() {
        frame.setName("铁车架");
        bike.setFrame(frame);
    }

    @Override
    public void builderSeat() {
        seat.setName("橡胶车座");
        bike.setSeat(seat);
    }

    @Override
    public Bike createBike() {
        return bike;
    }
}

/**
 * 指挥者类
 */
public class Director {

    private Builder builder;

    public Director(Builder builder){
        this.builder = builder;
    }

    //组装自行车
    public Bike construct(){
        builder.builderFrame();
        builder.builderSeat();
        return builder.createBike();
    }
}

public class Client {

    public static void main(String[] args) {
        Director director = new Director(new FeiGeBuilder());
        //组装自行车
        Bike bike = director.construct();
        System.out.println(bike.getFrame().getName());
        System.out.println(bike.getSeat().getName());
    }

}

你可能感兴趣的:(设计模式)