关于java:设计模式-创建型

55次阅读

共计 12675 个字符,预计需要花费 32 分钟才能阅读完成。

创立型模式次要解决对象的创立问题,封装简单的创立过程,解耦对象的创立代码和应用代码。

罕用的:单例模式、工厂模式(工厂办法和形象工厂)、建造者模式

不罕用的:原型模式

一、单例模式

一个类只容许创立一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式。

3 个要点

  1. 某个类只能有一个实例
  2. 它必须自行创立这个实例
  3. 它必须自行向整个零碎提供这个实例

实现形式

饿汉式

当类被加载时,动态变量 instance 会被初始化,此时类的公有构造函数会被调用,单例类的惟一实例将被创立。如果应用饿汉式单例来实现 ID 生成器 IdGenerator 类的设计,则不会呈现创立多个单例对象的状况,可确保单例对象的唯一性。

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();
}
}

懒汉式

懒汉式单例在第一次调用 getInstance()办法时实例化,在类加载时并不自行实例化,这种技术又称为提早加载(Lazy Load)技术,即须要的时候再加载实例。为了防止多个线程同时调用 getInstance()办法,能够应用关键字 synchronized。

该懒汉式单例类在 getInstance()办法后面减少了关键字 synchronized 进行线程锁定,以解决多个线程同时拜访的问题。上述代码尽管解决了线程平安问题,然而每次调用 getInstance()时都须要进行线程锁定判断,在多线程高并发拜访环境中,将会导致系统性能大大降低。

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();
}
}

双重检测

须要两次判空的起因:如果某一瞬间线程 A 和线程 B 都在调用 getInstance()办法,此时 instance 对象为 null 值,均能通过“instance==null”的判断。因为实现了 synchronized 加锁机制,线程 A 进入 synchronized 锁定的代码中执行实例创立代码,线程 B 处于排队期待状态,必须期待线程 A 执行结束后才能够进入 synchronized 锁定代码。但当 A 执行结束时,线程 B 并不知道实例曾经创立,将持续创立新的实例,导致产生多个单例对象,违反单例模式的设计思维。因而须要进行进一步改良,在 synchronized 锁定代码中再进行一次“instance==null”判断,这种形式称为双重查看锁定(Double-Check Locking)

如果应用双重查看锁定来实现懒汉式单例类,须要在动态成员变量 instance 之前减少修饰符 volatile,被 volatile 润饰的成员变量能够确保多个线程都可能正确处理,且该代码只能在 JDK 1.5 及以上版本中能力正确执行。因为 volatile 关键字会屏蔽 Java 虚拟机所做的一些代码优化,可能会导致系统运行效率升高

public class IdGenerator {private AtomicLong id = new AtomicLong(0);
private volatile 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();
}
}

动态外部类

因为动态单例对象没有作为 IdGenerator 的成员变量间接实例化,因而类加载时不会实例化 IdGenerator。第一次调用 getInstance()时将加载外部类 SingletonHolder,在该外部类中定义了一个 static 类型的变量 instance,此时会首先初始化这个成员变量,由 Java 虚拟机来保障其线程安全性,确保该成员变量只能初始化一次。因为 getInstance()办法没有被任何线程锁定,因而其性能不会造成任何影响。

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();
}
}

枚举

public enum IdGenerator {
INSTANCE;
private AtomicLong id = new AtomicLong(0);

public long getId() {return id.incrementAndGet();
}
}

实用场景

  1. 零碎只须要一个实例对象。例如,零碎要求提供一个惟一的序列号生成器或资源管理器,或者须要思考资源耗费太大而只容许创立一个对象。
  2. 客户调用类的单个实例只容许应用一个公共拜访点。除了该公共拜访点,不能通过其余路径拜访该实例。

二、工厂模式

工厂模式是最罕用的一类创立型设计模式。通常所说的工厂模式是指工厂办法模式,它也是应用频率最高的工厂模式。

1、简略工厂

定义一个工厂类,它能够依据参数的不同返回不同类的实例,被创立的实例通常都具备独特的父类。因为在简略工厂模式中用于创立实例的办法是动态(static)办法,因而简略工厂模式又被称为动态工厂办法(Static Factory Method)模式,它属于类创立型模式。

要点

当你须要什么,只须要传入一个正确的参数,就能够获取你所须要的对象,而无须晓得其创立的细节。

实现形式

办法一:

 public class RuleConfigSource {public RuleConfig load(String ruleConfigFilePath) {String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
     IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension);
     if (parser == null) {
       throw new InvalidRuleConfigException("Rule config file format is not supported:" + ruleConfigFilePath);
    }

     String configText = "";
     // 从 ruleConfigFilePath 文件中读取配置文本到 configText 中
     RuleConfig ruleConfig = parser.parse(configText);
     return ruleConfig;
  }

   private String getFileExtension(String filePath) {
     //... 解析文件名获取扩展名,比方 rule.json,返回 json
     return "json";
  }
}

 public class RuleConfigParserFactory {public static IRuleConfigParser createParser(String configFormat) {
     IRuleConfigParser parser = null;
     if ("json".equalsIgnoreCase(configFormat)) {parser = new JsonRuleConfigParser();
    } else if ("xml".equalsIgnoreCase(configFormat)) {parser = new XmlRuleConfigParser();
    } else if ("yaml".equalsIgnoreCase(configFormat)) {parser = new YamlRuleConfigParser();
    } else if ("properties".equalsIgnoreCase(configFormat)) {parser = new PropertiesRuleConfigParser();
    }
     return parser;
  }
}

办法二:

 public class RuleConfigParserFactory {private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>();

   static {cachedParsers.put("json", new JsonRuleConfigParser());
     cachedParsers.put("xml", new XmlRuleConfigParser());
     cachedParsers.put("yaml", new YamlRuleConfigParser());
     cachedParsers.put("properties", new PropertiesRuleConfigParser());
  }

   public static IRuleConfigParser createParser(String configFormat) {if (configFormat == null || configFormat.isEmpty()) {return null;// 返回 null 还是 IllegalArgumentException 全凭你本人说了算}
     IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase());
     return parser;
  }
}

核心内容

  1. RuleConfigParserFactory(工厂角色):即工厂类,它是简略工厂模式的外围,负责实现创立所有产品实例的外部逻辑。工厂类能够被外界间接调用,创立所需的产品对象。在工厂类中提供了动态的工厂办法 createParser(),它的返回类型为形象产品类型 IRuleConfigParser。
  2. IRuleConfigParser(形象产品角色):它是工厂类所创立的所有对象的父类或者接口,封装了各种产品对象的私有办法。形象产品的引入将进步零碎的灵活性,使得在工厂类中只需定义一个通用的工厂办法,因为所有创立的具体产品对象都是其子类对象或者其实现类对象。
  3. JsonRuleConfigParser(具体产品角色):它是简略工厂模式的创立指标,所有被创立的对象都充当这个角色的某个具体类的实例。每个具体产品角色都继承了或者实现了形象产品角色,须要实现在形象产品中申明的形象办法或者接口。

实用场景

  1. 工厂类负责创立的对象比拟少。因为创立的对象较少,不会造成工厂办法中的业务逻辑太过简单。
  2. 客户端只晓得传入工厂类的参数,对于如何创建对象并不关怀。

2、工厂办法

定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂办法模式让一个类的实例化提早到其子类。工厂办法模式又简称为工厂模式(Factory Pattern),又可称作虚构结构器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic FactoryPattern)。工厂办法模式是一品种创立型模式。

实现形式

 public interface IRuleConfigParserFactory {IRuleConfigParser createParser();
}

 public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
   @Override
   public IRuleConfigParser createParser() {return new JsonRuleConfigParser();
  }
}

 public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
   @Override
   public IRuleConfigParser createParser() {return new XmlRuleConfigParser();
  }
}

 public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
   @Override
   public IRuleConfigParser createParser() {return new YamlRuleConfigParser();
  }
}

 public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
   @Override
   public IRuleConfigParser createParser() {return new PropertiesRuleConfigParser();
  }
}

简略工厂创立工厂类对象

 public class RuleConfigSource {public RuleConfig load(String ruleConfigFilePath) {String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);

     IRuleConfigParserFactory parserFactory =RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension);
     if (parserFactory == null) {throw new InvalidRuleConfigException("Rule config file format is not supported:" + ruleConfigFilePath);
    }
     IRuleConfigParser parser = parserFactory.createParser();

     String configText = "";
     // 从 ruleConfigFilePath 文件中读取配置文本到 configText 中
     RuleConfig ruleConfig = parser.parse(configText);
     return ruleConfig;
  }

   private String getFileExtension(String filePath) {
     //... 解析文件名获取扩展名,比方 rule.json,返回 json
     return "json";
  }
}

 // 因为工厂类只蕴含办法,不蕴含成员变量,齐全能够复用,// 不须要每次都创立新的工厂类对象,所以,简略工厂模式的第二种实现思路更加适合。public class RuleConfigParserFactoryMap { // 工厂的工厂
   private static final Map<String, IRuleConfigParserFactory> cachedFactories = new HashMap<>();

   static {cachedFactories.put("json", new JsonRuleConfigParserFactory());
     cachedFactories.put("xml", new XmlRuleConfigParserFactory());
     cachedFactories.put("yaml", new YamlRuleConfigParserFactory());
     cachedFactories.put("properties", new PropertiesRuleConfigParserFactory());
  }

   public static IRuleConfigParserFactory getParserFactory(String type) {if (type == null || type.isEmpty()) {return null;}
     IRuleConfigParserFactory parserFactory = cachedFactories.get(type.toLowerCase());
     return parserFactory;
  }
}

核心内容

  1. IRuleConfigParser(形象产品):它是定义产品的接口,是工厂办法模式所创建对象的超类型,也就是产品对象的公共父类。
  2. JsonRuleConfigParser(具体产品):它实现了形象产品接口,某种类型的具体产品由专门的具体工厂创立,具体工厂和具体产品之间一一对应。
  3. IRuleConfigParserFactory(形象工厂):在形象工厂类中,申明了工厂办法(createParser),用于返回一个产品。形象工厂是工厂办法模式的外围,所有创建对象的工厂类都必须实现该接口。
  4. JsonRuleConfigParserFactory(具体工厂):它是形象工厂类的子类,实现了形象工厂中定义的工厂办法,并可由客户端调用,返回一个具体产品类的实例。

实用场景

  1. 客户端不晓得其所须要的对象的类。在工厂办法模式中,客户端不须要晓得具体产品类的类名,只须要晓得所对应的工厂即可,具体的产品对象由具体工厂类创立,可将具体工厂类的类名存储在配置文件或数据库中。
  2. 形象工厂类通过其子类来指定创立哪个对象。在工厂办法模式中,形象工厂类只须要提供一个创立产品的接口,而由其子类来确定具体要创立的对象,利用面向对象的多态性和里氏代换准则,在程序运行时,子类对象将笼罩父类对象,从而使得零碎更容易扩大。

3、形象工厂

提供一个创立一系列相干或相互依赖对象的接口,而无须指定它们具体的类。形象工厂模式又称为 Kit 模式,它是一种对象创立型模式。

实现形式

public interface IConfigParserFactory {IRuleConfigParser createRuleParser();
 ISystemConfigParser createSystemParser();
 // 此处能够扩大新的 parser 类型,比方 IBizConfigParser
}

public class JsonConfigParserFactory implements IConfigParserFactory {
 @Override
 public IRuleConfigParser createRuleParser() {return new JsonRuleConfigParser();
}

 @Override
 public ISystemConfigParser createSystemParser() {return new JsonSystemConfigParser();
}
}

public class XmlConfigParserFactory implements IConfigParserFactory {
 @Override
 public IRuleConfigParser createRuleParser() {return new XmlRuleConfigParser();
}

 @Override
 public ISystemConfigParser createSystemParser() {return new XmlSystemConfigParser();
}
}

// 省略 YamlConfigParserFactory 和 PropertiesConfigParserFactory 代码 

核心内容

  1. IConfigParserFactory(形象工厂):它申明了一组用于创立一族产品的办法,每个办法对应一种产品。
  2. JsonConfigParserFactory(具体工厂):它实现了在形象工厂中申明的创立产品的办法,生成一组具体产品,这些产品形成了一个产品族,每种产品都位于某个产品等级构造中。
  3. IRuleConfigParser(形象产品):它为每种产品申明接口,在形象产品中申明了产品所具备的业务办法。
  4. JsonRuleConfigParser(具体产品):它定义具体工厂生产的具体产品对象,实现在形象产品接口中申明的业务办法。

实用场景

  1. 一个零碎不该当依赖于产品类实例如何被创立、组合和表白的细节,这对于所有类型的工厂模式都是很重要的,用户毋庸关怀对象的创立过程,将对象的创立和应用解耦。
  2. 零碎中有多于一个的产品族,而每次只应用其中某一个产品族。能够通过配置文件等形式来使得用户能够动静扭转产品族,也能够很不便地减少新的产品族。
  3. 属于同一个产品族的产品将在一起应用,这一束缚必须在零碎的设计中体现进去。同一个产品族中的产品能够是没有任何关系的对象,然而它们都具备一些独特的束缚。例如同一操作系统下的按钮和文本框,按钮与文本框之间没有间接关系,但它们都是属于某一操作系统的,此时具备一个独特的约束条件:操作系统的类型。
  4. 产品等级构造稳固,设计实现之后,不会向零碎中减少新的产品等级构造或者删除已有的产品等级构造。

三、建造者模式

将一个简单对象的构建与它的示意拆散,使得同样的构建过程能够创立不同的示意。建造者模式是一种对象创立型模式。

实现形式

 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;
    }
  }
}

 // 这段代码会抛出 IllegalArgumentException,因为 minIdle>maxIdle
 ResourcePoolConfig config = new ResourcePoolConfig.Builder()
        .setName("dbconnectionpool")
        .setMaxTotal(16)
        .setMaxIdle(10)
        .setMinIdle(12)
        .build();

实用场景

  1. 须要生成的产品对象有简单的内部结构,这些产品对象通常蕴含多个成员变量。
  2. 须要生成的产品对象的属性相互依赖,须要指定其生成程序。
  3. 对象的创立过程独立于创立该对象的类。在建造者模式中通过引入指挥者类,将创立过程封装在指挥者类中,而不在建造者类和客户类中。
  4. 隔离简单对象的创立和应用,并使得雷同的创立过程能够创立不同的产品。

四、原型模式

如果对象的创立老本比拟大,而同一个类的不同对象之间差异不大(大部分字段都雷同),在这种状况下,咱们能够利用对已有对象(原型)进行复制(或者叫拷贝)的形式,来创立新对象,以达到节俭创立工夫的目标。这种基于原型来创建对象的形式就叫作原型设计模式,简称原型模式。

实现形式

 public class ConcretePrototype extends Prototype {
private String attr;// 成员变量

public void setAttr(String attr) {this.attr = attr;}
public String getAttr(){return this.attr;}
// 克隆办法
public Prototype clone() {Prototype prototype = new ConcretePrototype();// 创立新对象
prototype.setAttr(this.attr);
return prototype;
}
}

浅克隆

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是援用类型,则将援用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向雷同的内存地址。

深克隆

在深克隆中,无论原型对象的成员变量是值类型还是援用类型,都将复制一份给克隆对象,深克隆将原型对象的所有援用对象也复制一份给克隆对象。

实用场景

  1. 创立新对象老本较大(例如初始化须要占用较长的工夫,占用太多的 CPU 资源或网络资源)。新的对象能够通过原型模式对已有对象进行复制来取得,如果是类似对象,则能够对其成员变量稍作批改。
  2. 如果零碎要保留对象的状态,而对象的状态变动很小,或者对象自身占用内存较少时,能够应用原型模式配合备忘录模式来实现。
  3. 须要防止应用分档次的工厂类来创立分档次的对象,并且类的实例对象只有一个或很少的几个组合状态。通过复制原型对象失去新实例可能比应用构造函数创立一个新实例更加不便。

正文完
 0