单例模式(Singleton)

一个类只允许创建一个对象,那么这个类就是一个单例类。
实现一个单例,我们需要关注以下几个点。

  • 构造函数的访问权限为private;
  • 创建对象时的线程安全问题;
  • 是否支持延迟加载
  • getInstance()性能是否足够高

下面是Java中实现单例的几种经典方法,我们以一个简单的ID生成器为例。

饿汉式

  1. class Singleton{
  2. AtomicLong id=new AtomicLong(1);
  3. private Singleton(){}
  4. private static final Singleton instance=new Singleton();
  5. public static Singleton getInstance() {
  6. return instance;
  7. }
  8. public long getId(){
  9. return id.getAndIncrement();
  10. }
  11. }

双重检验锁

  1. class Singleton{
  2. AtomicLong id=new AtomicLong(1);
  3. private Singleton(){}
  4. private static volatile Singleton instance=null;
  5. public static Singleton getInstance() {
  6. if(instance==null){
  7. synchronized (Singleton.class){
  8. if(instance==null){
  9. instance=new Singleton();
  10. }
  11. }
  12. }
  13. return instance;
  14. }
  15. public long getId(){
  16. return id.getAndIncrement();
  17. }
  18. }

静态内部类

  1. class Singleton{
  2. AtomicLong id=new AtomicLong(1);
  3. private Singleton(){}
  4. private static class SingletonHolder{
  5. private static final Singleton instance=new Singleton();
  6. }
  7. public static Singleton getInstance() {
  8. return SingletonHolder.instance;
  9. }
  10. public long getId(){
  11. return id.getAndIncrement();
  12. }
  13. }

枚举

  1. enum Singleton{
  2. INSTANCE;
  3. private AtomicLong id=new AtomicLong(1);
  4. public long getId(){
  5. return id.getAndIncrement();
  6. }
  7. }

工厂模式(Factory)

工厂模式通常用于屏蔽创建复杂对象的具体细节。例如:在创建对象过程中会因为条件不同而创建不同的对象,创建完对象之后需要对对象进行一系列的初始化操作等等。
工厂模式分为三种更加细分的类型:简单工厂,工厂方法,抽象工厂。
下面是Java实现一个Parser工厂的例子。

简单工厂

  1. enum FormatDoc{
  2. JSON,XML,YAML;
  3. }
  4. abstract class Parser{
  5. public void parse(){
  6. throw new UnsupportedOperationException();
  7. }
  8. }
  9. class JsonParser extends Parser{}
  10. class XmlParser extends Parser{}
  11. class YamlParser extends Parser{}
  12. class ParserFactory{
  13. private ParserFactory(){}
  14. public static Parser getParser(FormatDoc type){
  15. switch (type){
  16. case JSON: return new JsonParser();
  17. case XML: return new XmlParser();
  18. case YAML:return new YamlParser();
  19. }
  20. return null;
  21. }
  22. }

在上面代码中每次调用getParser()都会创建新的Parser。为了节省内存和对象创建时间,我们可以将Parser事先缓存起来,这样一来调用getParser()就可以直接从缓存中获取到对象。

  1. class ParserFactory{
  2. private static final Map<FormatDoc,Parser> cache=new ConcurrentHashMap<>();
  3. static {
  4. cache.put(FormatDoc.JSON,new JsonParser());
  5. cache.put(FormatDoc.XML,new XmlParser());
  6. cache.put(FormatDoc.YAML,new YamlParser());
  7. }
  8. private ParserFactory(){}
  9. public static Parser getParser(FormatDoc type){
  10. if(type==null){
  11. return null;
  12. }
  13. return cache.get(type);
  14. }
  15. }

工厂方法

  1. interface IParserFactory{
  2. Parser getParser();
  3. }
  4. class JsonParserFactory implements IParserFactory{
  5. @Override
  6. public Parser getParser() {
  7. return new JsonParser();
  8. }
  9. }
  10. class XmlParserFactory implements IParserFactory{
  11. @Override
  12. public Parser getParser() {
  13. return new XmlParser();
  14. }
  15. }
  16. class YamlParserFactory implements IParserFactory{
  17. @Override
  18. public Parser getParser() {
  19. return new YamlParser();
  20. }
  21. }
  22. class ParserFactoryMap{
  23. private static final Map<FormatDoc,IParserFactory> cache=new ConcurrentHashMap<>();
  24. static {
  25. cache.put(FormatDoc.JSON,new JsonParserFactory());
  26. cache.put(FormatDoc.XML,new XmlParserFactory());
  27. cache.put(FormatDoc.YAML,new YamlParserFactory());
  28. }
  29. private ParserFactoryMap(){}
  30. public static IParserFactory getParserFactory(FormatDoc type){
  31. if(type==null){
  32. return null;
  33. }
  34. return cache.get(type);
  35. }
  36. }

在使用工厂方法时先调用getParserFactory()获取到工厂对象,再调用getParser()获取到Parser。
实际上,对于简单对象的创建简单工厂模式完全可以胜任,使用工厂方法之后代码变得更加复杂,更难以理解。那么简单工厂和工厂方法应当如何选择呢?我们可以看到工厂方法将Parser的创建剥离了,由此可见,如果对象的创建非常复杂,需要进行一系列的初始化操作,那么应当使用工厂方法而不是简单工厂。

抽象工厂

在简单工厂和工厂方法中,解析器类只会根据配置文件格式来进行分类。但如果类有两种分类方式,那么对于json,xml,yaml三种文件就会有6个工厂;如果类有三种分类方法就会有9个工厂,以此类推。针对这种特殊场景就需要用到抽象工厂,让一个工厂负责创建所有的JsonParser,XmlParser,YamlParser。即无论有多少种分类方式,都将它们放到一个工厂中创建。

建造者模式(Builder)

假设我们需要设计一个资源配置类ResourcePoolConfig,成员变量分别为name,maxTotal,maxIdle,minIdle,name为必填项,其余为选填项。实现这样一个资源配置类并不难,但是如果资源配置类中配置项特别多时,使用构造方法传入参数会导致参数列表变得很长,导致很多非常隐蔽的bug。通过构造函数设置必填项,setter()方法设置选填项,就能实现我们得需求。如果我们希望对象创建完成之后就不能修改内部属性值,这时候就不能再暴露ResourcePoolConfig的setter()方法了。那么这样一个资源配置类该怎么实现呢?这时候就可以使用建造者模式。

  1. class ResourcePoolConfig{
  2. private String name;
  3. private int maxTotal;
  4. private int maxIdle;
  5. private int minIdle;
  6. private ResourcePoolConfig(Builder builder){
  7. this.name=builder.name;
  8. this.maxTotal= builder.maxTotal;
  9. this.maxIdle=builder.maxIdle;
  10. this.minIdle= builder.minIdle;
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. public int getMaxTotal() {
  16. return maxTotal;
  17. }
  18. public int getMaxIdle() {
  19. return maxIdle;
  20. }
  21. public int getMinIdle() {
  22. return minIdle;
  23. }
  24. public static class Builder{
  25. private static final int DEFAULT_MAX_TOTAL=8;
  26. private static final int DEFAULT_MAX_IDLE=8;
  27. private static final int DEFAULT_MIN_IDLE=0;
  28. private String name;
  29. private int maxTotal=DEFAULT_MAX_TOTAL;
  30. private int maxIdle=DEFAULT_MAX_IDLE;
  31. private int minIdle=DEFAULT_MIN_IDLE;
  32. public ResourcePoolConfig build(){
  33. if(StringUtils.isBlank(name)){
  34. throw new IllegalArgumentException("name is uninitialized");
  35. }
  36. if(maxIdle>maxTotal){
  37. throw new IllegalArgumentException("maxIdle is greater than maxTotal");
  38. }
  39. if(minIdle>maxIdle){
  40. throw new IllegalArgumentException("minIdle is greater than maxIdle");
  41. }
  42. return new ResourcePoolConfig(this);
  43. }
  44. public Builder setName(String name) {
  45. this.name = name;
  46. return this;
  47. }
  48. public Builder setMaxTotal(int maxTotal) {
  49. if(maxTotal<0){
  50. throw new IllegalArgumentException("maxTotal is less than 0");
  51. }
  52. this.maxTotal = maxTotal;
  53. return this;
  54. }
  55. public Builder setMaxIdle(int maxIdle) {
  56. if(maxIdle<0) {
  57. throw new IllegalArgumentException("maxIdle is less than 0");
  58. }
  59. this.maxIdle = maxIdle;
  60. return this;
  61. }
  62. public Builder setMinIdle(int minIdle) {
  63. if(minIdle<0){
  64. throw new IllegalArgumentException("minIdle is less than 0");
  65. }
  66. this.minIdle = minIdle;
  67. return this;
  68. }
  69. }
  70. }

使用时像下面这样

  1. ResourcePoolConfig poolConfig=new ResourcePoolConfig.Builder()
  2. .setName("pool")
  3. .setMaxTotal(16)
  4. .setMaxIdle(8)
  5. .setMinIdle(4)
  6. .build();

是不是有一种很熟悉的感觉?在安卓开发中,常用这样的方法为Activity设置参数。

原型模式(Prototype)

所谓原型模式,即基于原型来创建对象,原型也是一个对象。通俗点说,就是通过对象来创建对象。如果对象创建的成本比较大,而同一个类的不同对象之间的差别不大,这时候我们就可以利用原型模式来创建对象。