单例模式(Singleton)
一个类只允许创建一个对象,那么这个类就是一个单例类。
实现一个单例,我们需要关注以下几个点。
- 构造函数的访问权限为private;
- 创建对象时的线程安全问题;
- 是否支持延迟加载
- getInstance()性能是否足够高
下面是Java中实现单例的几种经典方法,我们以一个简单的ID生成器为例。
饿汉式
class Singleton{AtomicLong id=new AtomicLong(1);private Singleton(){}private static final Singleton instance=new Singleton();public static Singleton getInstance() {return instance;}public long getId(){return id.getAndIncrement();}}
双重检验锁
class Singleton{AtomicLong id=new AtomicLong(1);private Singleton(){}private static volatile Singleton instance=null;public static Singleton getInstance() {if(instance==null){synchronized (Singleton.class){if(instance==null){instance=new Singleton();}}}return instance;}public long getId(){return id.getAndIncrement();}}
静态内部类
class Singleton{AtomicLong id=new AtomicLong(1);private Singleton(){}private static class SingletonHolder{private static final Singleton instance=new Singleton();}public static Singleton getInstance() {return SingletonHolder.instance;}public long getId(){return id.getAndIncrement();}}
枚举
enum Singleton{INSTANCE;private AtomicLong id=new AtomicLong(1);public long getId(){return id.getAndIncrement();}}
工厂模式(Factory)
工厂模式通常用于屏蔽创建复杂对象的具体细节。例如:在创建对象过程中会因为条件不同而创建不同的对象,创建完对象之后需要对对象进行一系列的初始化操作等等。
工厂模式分为三种更加细分的类型:简单工厂,工厂方法,抽象工厂。
下面是Java实现一个Parser工厂的例子。
简单工厂
enum FormatDoc{JSON,XML,YAML;}abstract class Parser{public void parse(){throw new UnsupportedOperationException();}}class JsonParser extends Parser{}class XmlParser extends Parser{}class YamlParser extends Parser{}class ParserFactory{private ParserFactory(){}public static Parser getParser(FormatDoc type){switch (type){case JSON: return new JsonParser();case XML: return new XmlParser();case YAML:return new YamlParser();}return null;}}
在上面代码中每次调用getParser()都会创建新的Parser。为了节省内存和对象创建时间,我们可以将Parser事先缓存起来,这样一来调用getParser()就可以直接从缓存中获取到对象。
class ParserFactory{private static final Map<FormatDoc,Parser> cache=new ConcurrentHashMap<>();static {cache.put(FormatDoc.JSON,new JsonParser());cache.put(FormatDoc.XML,new XmlParser());cache.put(FormatDoc.YAML,new YamlParser());}private ParserFactory(){}public static Parser getParser(FormatDoc type){if(type==null){return null;}return cache.get(type);}}
工厂方法
interface IParserFactory{Parser getParser();}class JsonParserFactory implements IParserFactory{@Overridepublic Parser getParser() {return new JsonParser();}}class XmlParserFactory implements IParserFactory{@Overridepublic Parser getParser() {return new XmlParser();}}class YamlParserFactory implements IParserFactory{@Overridepublic Parser getParser() {return new YamlParser();}}class ParserFactoryMap{private static final Map<FormatDoc,IParserFactory> cache=new ConcurrentHashMap<>();static {cache.put(FormatDoc.JSON,new JsonParserFactory());cache.put(FormatDoc.XML,new XmlParserFactory());cache.put(FormatDoc.YAML,new YamlParserFactory());}private ParserFactoryMap(){}public static IParserFactory getParserFactory(FormatDoc type){if(type==null){return null;}return cache.get(type);}}
在使用工厂方法时先调用getParserFactory()获取到工厂对象,再调用getParser()获取到Parser。
实际上,对于简单对象的创建简单工厂模式完全可以胜任,使用工厂方法之后代码变得更加复杂,更难以理解。那么简单工厂和工厂方法应当如何选择呢?我们可以看到工厂方法将Parser的创建剥离了,由此可见,如果对象的创建非常复杂,需要进行一系列的初始化操作,那么应当使用工厂方法而不是简单工厂。
抽象工厂
在简单工厂和工厂方法中,解析器类只会根据配置文件格式来进行分类。但如果类有两种分类方式,那么对于json,xml,yaml三种文件就会有6个工厂;如果类有三种分类方法就会有9个工厂,以此类推。针对这种特殊场景就需要用到抽象工厂,让一个工厂负责创建所有的JsonParser,XmlParser,YamlParser。即无论有多少种分类方式,都将它们放到一个工厂中创建。
建造者模式(Builder)
假设我们需要设计一个资源配置类ResourcePoolConfig,成员变量分别为name,maxTotal,maxIdle,minIdle,name为必填项,其余为选填项。实现这样一个资源配置类并不难,但是如果资源配置类中配置项特别多时,使用构造方法传入参数会导致参数列表变得很长,导致很多非常隐蔽的bug。通过构造函数设置必填项,setter()方法设置选填项,就能实现我们得需求。如果我们希望对象创建完成之后就不能修改内部属性值,这时候就不能再暴露ResourcePoolConfig的setter()方法了。那么这样一个资源配置类该怎么实现呢?这时候就可以使用建造者模式。
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;}public String getName() {return name;}public int getMaxTotal() {return maxTotal;}public int getMaxIdle() {return maxIdle;}public int getMinIdle() {return minIdle;}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("name is uninitialized");}if(maxIdle>maxTotal){throw new IllegalArgumentException("maxIdle is greater than maxTotal");}if(minIdle>maxIdle){throw new IllegalArgumentException("minIdle is greater than maxIdle");}return new ResourcePoolConfig(this);}public Builder setName(String name) {this.name = name;return this;}public Builder setMaxTotal(int maxTotal) {if(maxTotal<0){throw new IllegalArgumentException("maxTotal is less than 0");}this.maxTotal = maxTotal;return this;}public Builder setMaxIdle(int maxIdle) {if(maxIdle<0) {throw new IllegalArgumentException("maxIdle is less than 0");}this.maxIdle = maxIdle;return this;}public Builder setMinIdle(int minIdle) {if(minIdle<0){throw new IllegalArgumentException("minIdle is less than 0");}this.minIdle = minIdle;return this;}}}
使用时像下面这样
ResourcePoolConfig poolConfig=new ResourcePoolConfig.Builder().setName("pool").setMaxTotal(16).setMaxIdle(8).setMinIdle(4).build();
是不是有一种很熟悉的感觉?在安卓开发中,常用这样的方法为Activity设置参数。
原型模式(Prototype)
所谓原型模式,即基于原型来创建对象,原型也是一个对象。通俗点说,就是通过对象来创建对象。如果对象创建的成本比较大,而同一个类的不同对象之间的差别不大,这时候我们就可以利用原型模式来创建对象。
