设计模式之创建型模式

本文介绍了五种创建型设计模式,包括单例模式、工厂模式、抽象工厂模式、建造者模式及原型模式。每种模式均详细阐述了其实现方式、优缺点及适用场景。


创建型模式主要包括单例模式,工厂模式,抽象工厂模式,建造者模式以及原型模式

创建型模式(Creational Pattern)

该类型的设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,不是使用 new 运算符直接实例化对象,将软件模块中对象的创建和对象的使用分离。外界对于这些对象只需要知道它们的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。

1.1单例模式(Singleton Pattern)

单例模式中某个类在系统中只有一个实例,且只能由该类自己负责创建自己的唯一实例,并向外提供访问接口。该类被称为单例类。单例类要保证构造函数是私有的,并提供一个自身的静态私有成员变量以及一个共有的静态工厂方法向外提供唯一实例。

单例模式实现方式

1)懒汉式,线程不安全

//不支持多线程,严格意义上不算单例模式
public class Singleton {
  private static Singleton instance;
  private Singleton(){}
  public static Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

2)懒汉式,线程安全

//效率低,第一次调用时才初始化,避免内存浪费
public class Singleton {
  private static Singleton instance;
  private Singleton(){}
  public static synchronized Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

3)饿汉式

//类加载时就初始化,浪费内存,易产生垃圾对象
public class Singleton {
  private static Singleton instance = new Singleton();
  private Singleton(){}
  public static Singleton getInstance() {
    return instance;
  }
}

4)双重校验锁

//安全且在多线程情况下能保持高性能
public class Singleton {
  private volatile static Singleton instance;
  private Singleton(){}
  public static Singleton getInstance() {
    if(instance == null) {
      synchronized(Singleton.class) {
        if(instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

5)静态内部类

//利用classloader机制保证初始化instance时只有一个线程。方式3中singleton类被加载后instance就会被实例化。而该方式只有在显式调用getInstance方法后显式装载静态内部类,实例化instance。
public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}

单例模式优缺点

优点:系统中只有一个对象,节约系统资源,减少内存开销

缺点:不能扩展,单例类职责过重,一定程度上违背单一职责原则,单例类将创建和使用功能融在一起

单例模式适用场景

系统中只需要一个实例对象,比如要生产唯一序列号,设备管理器等

1.2工厂模式(Factory Pattern)

工厂模式也叫多态工厂模式。在工厂模式中,父类负责定义创建对象的接口,子类负责实现具体的实例。比如先定义一个抽象的汽车工厂类,然后定义具体的轿车,跑车工厂类来实现抽象汽车工厂类中定义的方法,将创建过程延迟到子类中进行。这样,如果出现新的车型如卡车时,只需要引入新的卡车工厂类即可,符合开闭原则。

工厂模式实现

public class FactoryPatternDemo {
  public static void main(String[] arg) {
    VehicleFactory factory = new VehicleFactory();
    
    //轿车
    Vehicle car = factory.getVehicle("car");
    car.run();
    //卡车
    Vehicle truck = factory.getVehicle("truck");
    truck.run();
  }
}


public class VehicleFactory {
  public Vehicle getVehicle(String type) {
    if(type == null) return null;
    else if(type.equals("car")) {
      return new Car();
    } else if(type.equals("truck")) {
      return new Truck();
    }
    return null;
  }
}


public interface Vehicle {
  public void run();
}
public class Car implements Vehicle {
  public void run() {
    System.out.print("Car is runing");
  }
}
public class Truck implements Vehicle {
  public void run() {
    System.out.print("Truck is runing");
  }
}

工厂模式的优缺点

优:扩展性高,只关注调用接口不关注具体实现细节,增加新功能无需修改源代码;

缺:每一个产品都需要一个具体的实现类,系统中类的个数成倍增加

工厂模式的适用场景

在任何需要复杂对象的场景中都可以使用工厂模式。比如,日志记录器,可以记录在本地磁盘,或远程服务器等;服务器连接框架,不同的协议实现不同的连接类。

1.3抽象工厂模式(Abstract Factory Pattern)

工厂模式是在具体实现上做了抽象得到工厂类,抽象工厂模式是在工厂类之上做了进一步抽象(工厂模式是先定义工厂,由工厂生产对象;抽象工厂模式是先定义抽象工厂,由抽象工厂生成具体工厂,再由具体工厂生成对象)。即抽象工厂模式是工厂的工厂。抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。相较于工厂模式,抽象工厂可以提供多个产品对象而非单一对象。

产品等级结构:即产品的继承结构。一个抽象类是电视,子类是具体品牌,则父类与子类构成了产品等级结构。
产品族:指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
产品族难扩展,产品等级易扩展。
工厂模式针对的是一个产品等级结构(一个工厂一种产品),而抽象工厂模式则需要面对多个产品等级结构(一个工厂多种产品)。当系统需要工厂生产的产品不是单一产品而是属于不同类型的产品时,需要使用抽象工厂。

抽象工厂模式的实现

public class AbstractFactoryPatternDemo {
  public static void main(String[] arg) {
    //创建一个手机工厂和一个电脑工厂
    AbstractFactory phoneFactory = FactoryProducer.getFactory("phone");
    AbstractFactory computerFactory = FactoryProducer.getFactory("computer");
    
    //生产一个智能机,一个老年机
    Phone smartPhone = phoneFactory.getPhone("smart");
    Phone keyBdPhone = phoneFactory.getPhone("senior");
    smartPhone.call();
    keyBdPhone.call();
    
    //生产一个商务本,一个游戏本
    Computer commercialPC = compuerFactory.getPC("commercial");
    Computer gamePC = compuerFactory.getPC("game");
    commercialPC.sendMail();
    gamePC.sendMail();
  }
}


public class FactoryProducer {
  public static AbstractFactory getFactory(String type) {
    if(type.equals("phone")) {
      return new PhoneFactory();
    } else if(type.equals("computer")) {
      return new ComputerFactory();
    }
    return null;
  }
}


public abstract class AbstractFactory {
  //抽象类与接口的区别
  //语法上,大致无异。接口里的变量是用public static final修饰的
  //应用上,抽象类是类,是对事物的抽象,接口一般是公共的属性和方法
  public abstract Phone getPhone(String type);
  public abstract Computer getPC(String type);
}


//手机工厂
public class PhoneFactory extends AbstractFactory {
  public Computer getPC(String type) {
    return null;
  }
  public Phone getPhone(String type) {
    if(type.equals("smart")) {
      return new SmartPhone();
    } else if(type.equals("senior")) {
      return new SeniorPhone();
    }
    return null;
  }
}
public interface Phone {
  public void call();
}
public class SmartPhone implements Phone {
  public void call() {
    System.out.print("smart phone");
  }
}
public class SeniorPhone implements Phone {
  public void call() {
    System.out.print("senior phone");
  }
}


//电脑工厂
public class ComputerFactory extends AbstractFactory {
  public Computer getPC(String type) {
    if(type.equals("commercial")) {
      return new CommercialPC();
    } else if(type.equals("game")) {
      return new GamePC();
    }
    return null;
  }
  public Phone getPhone(String type) {
    return null;
  }
}
public interface Computer {
  public void snedMail();
}
public class CommercialPC implements Computer {
  public void sendMail() {
    System.out.print("commercial");
  }
}
public class GamePC implements Computer {
  public void sendMail() {
    System.out.print("game");
  }
}

抽象工厂模式的优缺点

当一个产品族中的多个对象被设计成一起工作时,能保证客户端始终只使用同一个产品族中的对象。利于需要根据当前环境来决定其行为的软件系统
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”
不利于增加新类型的工厂即纵向易扩展,横向难改变
抽象工厂模式的使用场景

系统中有多个产品族,每次只使用其中的一个。

1.4建造者模式(Builder Pattern)

建造者模式使用多个简单的对象一步一步构建成一个复杂的对象,通过将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。用户不需要了解复杂对象的具体细节,只需提供复杂对象的类型和内容信息就可构建复杂对象。每一个建造者是相对独立的,可以很方便替换或增添新的建造者,符合开闭原则。

建造者模式包含四个角色:抽象建造者为创建一个产品对象的各个部件指定抽象接口;具体建造者实现了抽象建造者接口;产品角色是被构建的复杂对象;指挥者负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在指挥者construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。指挥者类的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程,指挥者针对抽象建造者编程。

如果系统中只有一个建造者的话,可以省略抽象建造者;若抽象建造者被省略可进一步省略指挥者。
在这里插入图片描述

建造者模式的实现

对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。

public class BuilderPatternDemo {
  public static void main(String[] arg) {
    ProductOneBuilder builderOne = new ProductOneBuilder();
    Director director = new Director(ProductOneBuilder);
    Product p = director.construct();
    
    ProductTwoBuilder builderTwo = new ProductTwoBuilder();
    director.setDirector(builderTwo);
    Product pp = director.construct();
  }
}


public class Product {
  public String name;
  public String type;
}


public abstract class Builder {
  private Product product = new Product();
  public abstract void buildPartA();
  public abstract void buildPartB();
  public Product getResult() {
    return product;
  }
}


public class ProductOneBuilder extends Builder {
  public void buildPartA() {
    System.out.print("partA of Product one");
  }
  public void buildPartB() {
    System.out.print("partB of Product one");
  }
} 
public class ProductTwoBuilder extends Builder {
  public void buildPartA() {
    System.out.print("partA of Product two");
  }
  public void buildPartB() {
    System.out.print("partB of Product two");
  }
} 


public class Director {
  Builder builder = null;
  public Director(Builde builder) {
    this.builder = builder;
  }
  public void setDirector(Builder builder) {
    this.builder = builder;
  }
  //具体的构造过程
  public Product construct() {
    builder.buildPartA();
    builder.buildPartB();
    return build.getResult();
  }
}

建造者模式的优缺

优:客户无需了解产品内部细节,将产品自身与产品创建过程解耦,相同的创建过程可以创建不同的产品对象

缺:建造者模式创建的产品内部组件相似,有共同点,内部属性相互依赖需要指定生成顺序,若内部之间差异大则不宜用。

建造者模式的适用场景

对象内部结构复杂或内部属性有依赖需指定生成顺序。

1.5原型模式(Prototype Pattern)

当直接创建对象的代价较高时,可以采用原型模式创建重复对象。原型模式通过实现一个原型接口利用已有的原型对象来创建当前对象的克隆。原型模式是通过拷贝一个现有对象生成新对象。java中浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

原型模式的实现

public class Sheep implements Cloneable {
  private String name;
  public Sheep(String name) {
    this.name = name;
  }
  public String getName() {
    return this.name;
  }
  public void setName(String name) {
    this.name = name;
  }
  
  public Object clone() {
    Object clone = null;
    try {
      clone = super.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return clone;
  }
}


public class PrototypeDemo {
  public static void main(String[] arg) {
    Sheep dolly = new Sheep("Dolly");
    Sheep cloneSheep = (Sheep)dolly.clone();
  }
}
//深拷贝
public class Sheep implements Serializable,Cloneable {
  private String name;
  private Sheep father;
  public Sheep(String name) {
    this.name = name;
    this.faher = null;
  }
  public Sheep(String name, Sheep father) {
    this.name = name;
    this.father = father;
  }
  public String getName() {
    return this.name;
  }
  public void setName(String name) {
    this.name = name;
  }
  
  //方式一
  public Object clone() {
    Object clone = null;
    try {
      clone = super.clone();
      Sheep vice = (Sheep) clone;
      vice.father = (Sheep) father.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return clone;
  }
  
  //方式二
  public Object deepClone() {
    Object clone = null;
    ByteArrayOutputStream bos = null;
    ByteArrayInputStream bis = null;
    ObjectOutputStream oos = null;
    ObjectInputStream ois = null;
    
    try {
      bos = new ByteArrayOutputStream();
      oos = new ObjectOutputStream(bos);
      oos.writeObject(this);
      
      bis = new ByteArrayInputStream(bos.toByArray());
      ois = new ObjectInputStream(bis);
      clone = ois.readObject();
    } catch (Exception e) {
      e.printStackTree();
    } finally {
      try {
        bos.close();
        oos.close();
        bis.close();
        ois.close();
      } catch (Exception e) {
         e.printStackTree();
      }
    }
    return clone;
  }
}


public class PrototypeDemo {
  public static void main(String[] arg) {
    Sheep dolly = new Sheep("Dolly", new Sheep("Mr.wang"));
    Sheep cloneSheep1 = (Sheep)dolly.clone();
    Sheep cloneSheep2 = (Sheep)dolly.deepClone();
  }
}

原型模式的优缺

优:性能提高,避开构造函数约束,

缺:克隆时需要考虑原对象所有功能,尤其当原对象引用了不支持序列化的间接对象或者包含循环结构时

原型模式的适用场景

创建对象时需要的资源多;或者一个对象多个修改者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小源er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值