【重温设计模式】创建型-单例模式,工厂模式,建造者模式,原型模式
一、创建型设计模式
- 单例模式
- 工厂模式
- 建造者模式
- 原型模式
二、单例模式
1、为什么要使用单例模式
- 有些数据在系统中只应该保存一份,就比较适合设计为单例类。比如,系统的配置信息类。
- 我们还可以使用单例解决资源访问冲突的问题。
2、单例模式的类型
- 饿汉式
- 懒汉式
- 双重检测
- 静态内部类
- 枚举
3、单例模式的示例
1、饿汉式(不支持延迟加载)
import java.util.concurrent.atomic.AtomicLong; public class IdGenerator { private AtomicLong id = new AtomicLong(0); private static final IdGenerator instance = new IdGenerator(); private IdGenerator() { } public static IdGenerator getInstance() { return instance; } public long getId() { return id.incrementAndGet(); } }
2、懒汉式(锁的粒度粗,并发性不好,支持延迟加载)
import java.util.concurrent.atomic.AtomicLong; public class IdGenerator { private AtomicLong id = new AtomicLong(0); private static IdGenerator instance; private IdGenerator() { } public static synchronized IdGenerator getInstance() { if (instance == null) { instance = new IdGenerator(); } return instance; } public long getId() { return id.incrementAndGet(); } }
3、双重检测
import java.util.concurrent.atomic.AtomicLong; public class IdGenerator { private AtomicLong id = new AtomicLong(0); private static IdGenerator instance; private IdGenerator() { } public static IdGenerator getInstance() { if (instance == null) { synchronized (IdGenerator.class) { // 此处为类级别的锁 if (instance == null) { instance = new IdGenerator(); } } } return instance; } public long getId() { return id.incrementAndGet(); } }
4、静态内部类
SingletonHolder 是一个静态内部类,当外部类 IdGenerator 被加载的时候,并不会创建 SingletonHolder 实例对象。只有当调用 getInstance() 方法时,SingletonHolder 才会被加载,这个时候才会创建 instance。instance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载。
import java.util.concurrent.atomic.AtomicLong; public class IdGenerator { private AtomicLong id = new AtomicLong(0); private IdGenerator() { } private static class SingletonHolder { private static final IdGenerator instance = new IdGenerator(); } public static IdGenerator getInstance() { return SingletonHolder.instance; } public long getId() { return id.incrementAndGet(); } }
5、枚举
基于枚举类型的单例实现。这种实现方式通过 Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性
import java.util.concurrent.atomic.AtomicLong; public enum IdGenerator { INSTANCE; private AtomicLong id = new AtomicLong(0); public long getId() { return id.incrementAndGet(); } }
三、工厂模式
1、为什么要使用工厂模式
- 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
- 代码复用:创建代码抽离到独立的工厂类之后可以复用。
- 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
- 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
2、工厂模式的类型
- 简单工厂
- 工厂方法
- 抽象工厂
3、工厂模式的实例
1、简单工厂(一个工厂类基于入参创建出不同的产品)
public interface Shape { void draw(); } public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } public class Square implements Shape { @Override public void draw() { System.out.println("Inside Square::draw() method."); } } public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } public class ShapeFactory { //使用 getShape 方法获取形状类型的对象 public Shape getShape(String shapeType){ if(shapeType == null){ return null; } if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); } else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle(); } else if(shapeType.equalsIgnoreCase("SQUARE")){ return new Square(); } return null; } }
2、工厂方法(基于接口定义工厂接口,将不同的对象创建单独申请一个工厂类,消除简单工厂的if else逻辑)
package com.sxf.study.factory; public interface ParserFactory { /** * 创建Parser * @return */ Parser createParser(); } public class CvsParserFactory implements ParserFactory { @Override public Parser createParser() { return new CvsParser(); } } public class XmlParserFactory implements ParserFactory{ @Override public Parser createParser() { return new XmlParser(); } } public interface Parser { /** * 解析配置 * @param config * @return */ String parse(String config); } public class XmlParser implements Parser{ @Override public String parse(String config) { return null; } } public class CvsParser implements Parser{ @Override public String parse(String config) { return null; } }
3、抽象工厂(工厂的工厂)
//形状接口 public interface Shape { void draw(); } public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } public class Square implements Shape { @Override public void draw() { System.out.println("Inside Square::draw() method."); } } public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } //颜色接口 public interface Color { void fill(); } public class Red implements Color { @Override public void fill() { System.out.println("Inside Red::fill() method."); } } public class Green implements Color { @Override public void fill() { System.out.println("Inside Green::fill() method."); } } public class Blue implements Color { @Override public void fill() { System.out.println("Inside Blue::fill() method."); } } //定义工厂类 public abstract class AbstractFactory { public abstract Color getColor(String color); public abstract Shape getShape(String shape) ; } //形状工厂 public class ShapeFactory extends AbstractFactory { @Override public Shape getShape(String shapeType){ if(shapeType == null){ return null; } if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); } else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle(); } else if(shapeType.equalsIgnoreCase("SQUARE")){ return new Square(); } return null; } @Override public Color getColor(String color) { return null; } } //颜色工厂 public class ColorFactory extends AbstractFactory { @Override public Shape getShape(String shapeType){ return null; } @Override public Color getColor(String color) { if(color == null){ return null; } if(color.equalsIgnoreCase("RED")){ return new Red(); } else if(color.equalsIgnoreCase("GREEN")){ return new Green(); } else if(color.equalsIgnoreCase("BLUE")){ return new Blue(); } return null; } } //工厂提供器(工厂的工厂) public class FactoryProducer { public static AbstractFactory getFactory(String choice){ if(choice.equalsIgnoreCase("SHAPE")){ return new ShapeFactory(); } else if(choice.equalsIgnoreCase("COLOR")){ return new ColorFactory(); } return null; } }
四、建造者模式
1、为什么要使用建造者模式
- 工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象
- 建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。
- 顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉。对于披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨。
2、建造者模式示意(mybatis的XMLMapperBuilder)
import org.apache.commons.lang3.StringUtils; public class ResourcePoolConfig { private String name; private int maxTotal; private int maxIdle; private int minIdle; private ResourcePoolConfig(Builder builder) { this.name = builder.name; this.maxTotal = builder.maxTotal; this.maxIdle = builder.maxIdle; this.minIdle = builder.minIdle; } //...省略getter方法... // 我们将Builder类设计成了ResourcePoolConfig的内部类。 // 我们也可以将Builder类设计成独立的非内部类ResourcePoolConfigBuilder。 public static class Builder { private static final int DEFAULT_MAX_TOTAL = 8; private static final int DEFAULT_MAX_IDLE = 8; private static final int DEFAULT_MIN_IDLE = 0; private String name; private int maxTotal = DEFAULT_MAX_TOTAL; private int maxIdle = DEFAULT_MAX_IDLE; private int minIdle = DEFAULT_MIN_IDLE; public ResourcePoolConfig build() { // 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等 if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("..."); } if (maxIdle > maxTotal) { throw new IllegalArgumentException("..."); } if (minIdle > maxTotal || minIdle > maxIdle) { throw new IllegalArgumentException("..."); } return new ResourcePoolConfig(this); } public Builder setName(String name) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("..."); } this.name = name; return this; } public Builder setMaxTotal(int maxTotal) { if (maxTotal <= 0) { throw new IllegalArgumentException("..."); } this.maxTotal = maxTotal; return this; } public Builder setMaxIdle(int maxIdle) { if (maxIdle < 0) { throw new IllegalArgumentException("..."); } this.maxIdle = maxIdle; return this; } public Builder setMinIdle(int minIdle) { if (minIdle < 0) { throw new IllegalArgumentException("..."); } this.minIdle = minIdle; return this; } } } } // 这段代码会抛出IllegalArgumentExcept ResourcePoolConfig config=new ResourcePoolConfig.Builder() .setName("dbconnectionpool") .setMaxTotal(16) .setMaxIdle(10) .setMinIdle(12) .build();
五、原型模式
1、为什么要使用原型模式
- 对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式,来创建新对象,以达到节省创建时间的目的
- 原型模式分为:深拷贝和浅拷贝。
- 浅拷贝只会复制对象中基本数据类型数据和引用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象。
- 深拷贝得到的是一份完完全全独立的对象。所以,深拷贝比起浅拷贝来说,更加耗时,更加耗内存空间。
2、原型模式的示意
(1)浅拷贝
public class DataObject implements Cloneable{ private String name; private int age; private Long money; private List<Address> addressList=new ArrayList<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Long getMoney() { return money; } public void setMoney(Long money) { this.money = money; } public List<Address> getAddressList() { return addressList; } public void setAddressList(List<Address> addressList) { this.addressList = addressList; } /** *false *age=>12 *name=>sxf *obj name hashcode=>114337 *obj1 name Hashcode=>114337 *money=>9999 *adderess=>true * **/ public static void main(String[] args) throws CloneNotSupportedException { DataObject dataObject=new DataObject(); String name="sxf"; dataObject.setAge(12); dataObject.setName(name); dataObject.setMoney(9999L); Address address1=new Address(); address1.setA("a1"); address1.setB("b1"); Address address2=new Address(); address2.setA("a2"); address2.setB("b2"); List<Address> addresses=new ArrayList<>(); addresses.add(address1); addresses.add(address2); dataObject.setAddressList(addresses); DataObject dataObject1= (DataObject) dataObject.clone(); //浅拷贝 System.out.println(dataObject.equals(dataObject1)); System.out.println("age=>"+dataObject1.getAge()); System.out.println("name=>"+dataObject1.getName()); System.out.println("obj name hashcode=>"+name.hashCode()); System.out.println("obj1 name Hashcode=>"+ dataObject1.getName().hashCode()); System.out.println("money=>"+dataObject1.getMoney()); System.out.println("adderess=>"+dataObject1.getAddressList().equals(addresses)); } } class Address{ private String a; private String b; public String getA() { return a; } public void setA(String a) { this.a = a; } public String getB() { return b; } public void setB(String b) { this.b = b; } }
(2)深拷贝
package com.sxf.study.factory; import java.util.ArrayList; import java.util.List; /** * @author <a href="mailto:shangxiaofei@meituan.com">尚晓飞</a> * @date 2:33 PM 2020/7/26 */ public class DataObject implements Cloneable{ private String name; private int age; private Long money; private List<Address> addressList=new ArrayList<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Long getMoney() { return money; } public void setMoney(Long money) { this.money = money; } public List<Address> getAddressList() { return addressList; } public void setAddressList(List<Address> addressList) { this.addressList = addressList; Address address=null; } @Override protected DataObject clone() throws CloneNotSupportedException { DataObject other= (DataObject) super.clone(); other.addressList= (List<Address>) ((ArrayList<Address>)this.addressList).clone(); return other; } } class Address implements Cloneable{ private String a; private String b; public String getA() { return a; } public void setA(String a) { this.a = a; } public String getB() { return b; } public void setB(String b) { this.b = b; } }