前言
23种设计模式中5种创建模式。其分别是单例模式、工厂模式、抽象工厂模式、建造者模式和原型模式。其中,单例模式、工厂模式和抽象工厂模式主要用于项目刚初始的架构;建造者模式主要用于new
出一个新的对象,并为其赋予某些特殊的属性(详见建造者模式章节);原型模式主要用于拷贝创建。
单例模式
单例模式(Singleton Pattern)是一个比较简单的模式,其定义如下:
Ensure a class has only one instance, and provide a global point of access to it.(确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)
- 通用类图(饿汉模式)
# Class Singleton
public class Singleton{
private static final Singleton singleton = new Singleton();
// 限制生成多个对象
private Singleton {}
// 对外开放接口
public static Singleton getSingleton(){return singleton;}
// 类中的其它方法 尽量是static的
public static void doSomething(){}
}
- Case 1(单一皇帝)
# Emperor-class
class Emperor{
private final static Emperor emperor = new Emperor();
private Emperor(){}
public static Emperor getInstance(){
return emperor;
}
public static void say(){
System.out.println("emperor-XX");
}
}
class Minister{
public void meetEmperor(){
Emperor emperor = Emperor.getInstance();
emperor.say();
}
}
- 优点&缺点&场景
优点: 单例,减少内存开支;单例,统一访问点;单例,减少资源的重用。
缺点: 扩展困难,单例模式的接口毫无意义,因为其要求自行实例化;测试困难;和依赖倒置原则冲突。
场景: 需要单个对象,或统一访问的时候。(Spring类SqlFactory等)
单个对象消耗资源过多时,这个可以享元,多例解决。(比如数据库连接池,维护多例接口)
- 注意事项
在使用懒汉模式
的时候,需要特别注意线程安全问题。更多建议看下这篇文章:单例模式的七种写法
- 扩展
多例模式。(多例是否就是享元?)
- 个人理解
单例模式,故名思义,就是需要单个例子的场景。在项目构建之初还是使用比较多的。比如Spring类每个Bean都是单例的;另外,比如SqlFactory这些都是需要使用单例进行初始化的,多例不利于项目资源的管理和维护。
package xx.config; import java.beans.PropertyVetoException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import com.mchange.v2.c3p0.ComboPooledDataSource; @Configuration public class DataSourceConfig { @Autowired private Environment env; @Bean(name="dataSource") public ComboPooledDataSource dataSource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass(env.getProperty("jdbc.driverClassName")); dataSource.setJdbcUrl(env.getProperty("jdbc.url")); dataSource.setUser(env.getProperty("jdbc.username")); dataSource.setPassword(env.getProperty("jdbc.password")); dataSource.setMaxPoolSize(20); dataSource.setMinPoolSize(5); dataSource.setInitialPoolSize(10); dataSource.setMaxIdleTime(300); dataSource.setAcquireIncrement(5); dataSource.setIdleConnectionTestPeriod(60); return dataSource; } }
工厂模式
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)
- 通用类图(反射方式)
# 产品类
abstract class Product{
public void method1(){}
public abstract void method2();
}
public ConcreteProduct1 extends Product{
public void method2(){}
}
public ConcreteProduct2 extends Product{
public void method2(){}
}
# 工厂类
abstract class Creator{
public abstract <T extends Product> T createProduct(Class<T> c);
}
public class ConcreteCreator extends Creator{
public <T extends Product> T createProduct(Class<T> c){
Product product = null;
try{
product = (Product)Class.forName(c.getName()).newInstance();
}catch(Exception e){
e.printStack();
}
return (T)product;
}
}
# Client
public class Client{
public void main(String []args){
Creator creator = new ConcreteCreator();
Product product1 = creator.createProduct(ConcreteProduct1.class);
// 继续业务处理
}
}
- Case 1 工厂模式-女娲造人
# Human-interface
interface Human(){
public void getColor();
public void talk();
}
# BlackHuman-class
class BlackHuman implemenets Human{
public void getColor(){
System.out.println("Black Color");
}
public void talk(){
System.out.println("Black Human Language.");
}
}
# YelloHuman-class
# WhiteHuman-class
# AbstractHumanFactory-abstract class
abstract class AbstractHumanFactory{
public abstract <T extends Human> createHuman(Class<T> c);
}
# HumanFactory-class
class HumanFactory extends AbstractHumanFactory{
public Human createHuman(Class c){
Human human = null;
try{
human = (T)Class.forName(c.getName()).newInstance();
}catch(Exeception e){
System.out.println("生产错误!");
}
return human;
}
}
# NvWa-client class
class NvWa{
public static void main(String []args){
AbstractHumanFactory abstractHumanFactory = new HumanFactory();
Human human1 = abstractHumanFactory.createHuman(BlackHuman.class);
human1.getColor();human1.talk();
Human human2 = abstractHumanFactory.createHuman(YelloHuman.class);
Human human3 = abstractHumanFactory.createHuman(WhiteHuman.class);
}
}
- 优点 & 缺点 & 使用场景
优点:
- 良好的封装性,代码结构清晰。(调用某个产品类,只需要知道产品的类名即可。)
- 工厂模式的方法扩展性非常好。(只需要简单更改具体的工厂类,或扩展一个工厂类即可实现变化。比如新的产品类,本工厂几乎不需要更改。)
- 屏蔽产品类。(JDBC->MySQL切换Oracle 只需要更改名称。具体实现不需要知晓。)
- 典型的解耦框架。(符合 迪米特法则/依赖倒置原则/里氏替换原则)
缺点: 部分场景可能不需要。过度设计,增加复杂度。
使用场景:
new Product()
的替代品。需要根据产品规模决定是否需要使用工厂模式。- 需要灵活的、可扩展的框架时,可以考虑工厂方法。(比如邮箱服务 提供POP3/IMAP/HTTP协议服务。增加新的服务协议,只需要实例化新协议即可。)
- 异构项目中。(跨平台文件(Java-Python) 当作产品类。)
- 测试驱动框架中,实例化生成某些依赖的对象。(因JMoke的出现弱化。)
- 扩展
- 扩展1-缩小为简单工厂模式
- 扩展2-抽象工厂模式
- 扩展3-替代Singleton单例模式
- 扩展4-延迟加载工厂类
- 扩展1-缩小为简单工厂模式
- 扩展2-升级多个工厂类
- 扩展3-替代Singleton单例模式
# class Singleton
class Singleton{
private Singleton(){}
public void doSomrthing(){}
}
# class SingletonFactory
class SingletonFactory{
priate static Singleton singleton;
static {
try{
Class c1 = Class.forName(Singleton.class.getName());
// 获得无参数构造
Constructor constructor = c1.getDeclaredConstructor();
// 无参构造时可访问的
constructor.setAccessible(true);
// 生产一个实例对象
singleton = (Singleton) constructor.newInstance();
}catch(Exception e){
// 异常处理
}
}
public static Singleton getSingleton(){
return singleton;
}
}
- 扩展4-延迟初始化(Lazy Initilization)
# class ProductFactory
class ProductFactory{
public static final Map<String,Product> prMap = new HashMap();
public static synchronized Product createProductString type){
ConcreteFactory concreteFactory = new ConcreteFactory();
Product product = null;
if(prMap.containsKey(type)){
product = prMap.get("type");
}else {
product = concreteFactory.createProduct(type);
prMap.put(type, product);
}
return product;
}
}
定义一个Map容器,容纳所以产生的对象。如果在Map容器中已经存在的对象,则直接返回取出推出,如果没有,则生成一个对象放入Map中,以便于下次调用。(类似Spring?)
延迟加载是可以扩展的,例如限制一个产品类的最大实例化对象数量。通过判断Map内已有对象的数量判断。实例:JDBC最大链接数量MaxConnectionNum最大链接数,即为内存内最大实例化对象的数目。
延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,涉及多方面的交互,可以使用延迟加载降低对象的产生和销毁带来的复杂性。
- 最佳实践
注意工厂方法实施的场景。另外,工厂方法还经常和其它模式混合使用(例如模板方法、单例模式、原型模式等等)。
- 个人理解
个人理解,工厂模式是创建的不可少的一环。记得上学时候学的时候叫着
Java Bean工厂
,当时对Java Bean
很疑惑。如今,一晃多年,有见到了这位老朋友了。工厂模式作为创建模式的一种,常用来管理需要创建多种对象的情况。在实践中,项目最初创建的时候使用较多。另外,作者本章中将抽象工厂和工厂混为一谈了。
前段时间因为编译OpenJDK耗费了一些时间,导致设计模式搁置一段时间。今天重新更新这部分的内容,争取这两天的时间,把这部分内容全部更新完毕。2018.12.06
抽象工厂模式 (Abstract Factory Pattern)
抽象工厂模式是对于工厂模式的扩展。当一个工厂包含多个产品线的时候,可以使用抽象工厂模式。
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.(为创建一组相关或相互依赖的对象提供一个接口,而且无须指定他们的具体类。)
- 抽象工厂模式-通用类图
#AbstractProductA-abstract class
public abstract class AbstractProductA{
// 公用方法
public void shareMeothod(){}
// 私有方法
public void doSomething();
}
#ProductA1-class
class ProductA1 extends AbstractProductA{
public void doSomething(){System.out.println("Product A1");}
}
#ProductA2-class
class ProductA2 extends AbstractProductA{
public void doSomething(){System.out.println("Product A2");}
}
# AbstractProductB- abstract class
# ProductB1-class
# ProductB2-class
# AbstractCreator-abstract class
public abstract class AbstractCreator{
// A 产品线
public abstract AbstractProductA createProductA();
// B 产品线
public abstract AbstractProductB createProductB();
}
# Creator1-class
public class Creator1 extends AbstractCreator{
// A 产品线
public AbstractProductA createProductA(){return new ProductA1();}
// B 产品线
public AbstractProductB createProductB(){return new ProductB1();}
}
# Creator2-class
public class Creator2 extends AbstractCreator{
// A 产品线
public AbstractProductA createProductA(){return new ProductA2();}
// B 产品线
public AbstractProductB createProductB(){return new ProductB2();}
}
# 双工厂
public class client{
public static void main(String args[]){
AbstractCreator creator1 = new Creator1();
AbstractCreator creator2 = new Creator2();
// 1产品工厂
AbstractProductA a1 = creator1.createProductA();
AbstractProductB b1 = creator1.createProductB();
// 2产品工厂
AbstractProductA a2 = creator2.createProductA();
AbstractProductB b2 = creator2.createProductB();
}
}
- Case1
另: 书中提及的男女工厂,实在没有太多的意思,这边就将类图绘制于此。
- 优点 & 缺点 & 使用场景
优点:
- 封装性,与抽象工厂一致。只需要知道产品的名字即可通过工厂创建一个对象。
- 产品族内约束非公开。即,可以在产品创建的时候以一定比例进行约束。(比如A和B产品以1:2的比例进行约束,在实现类
Creator1
中约束即可。)
缺点:
- 缺点非常明显,扩展性差。比如需要增加产品线C,要么在
AbstractCreator
内添加createProductC
方法。要么重新创建一个ExtendAbstractCreator
然后进入createProductC
方法。第一种方法,违反开闭原则
。第二种方法,需要增加的代码量较大。后续扩展较为麻烦,比如增加产品线D/E/F/G,岂不是要加那么多方法类。使用场景:
多个产品族,并且产品直接具有相关约束时。注意事项:
单个产品线内,产品种类扩展不困难。但是产品线本身扩展困难。不要混淆。
- 最佳实践
不同的产品线。不同产品线个人感觉是指,产品具有不同的区分纬度。比如
区分1: 男人、女人;
区分2: 白人,黑人、黄种人。
类别包括:白人男人、白人女人,等等。如果产品作为一个个单独的个体,工厂模式是可以胜任的。但是,如果需要添加,比如男女比例3:2,这样就无法实现了,因为多个类别造成了类别区分的混乱。
- 个人理解
1 . 抽象工厂模式和工厂模式的区别?
工厂模式和抽象工厂设计模式的最主要的区别在于,工厂内含有几条产品线。工厂模式为单条,抽象工厂模式为多条。最直观的区别方法在于
AbstractCreator
抽象工厂方法内有几个createProduct
的方法即可。划分产品的等级不同,维度不同。2 . 约束,比如男人和女人比例1:2如何判断和实现?
client.java
端进行控制,不推荐;工厂类中进行控制,比如创建一个synchronized int a=0;synchronized int b=0;
进行计数进行控制;或者两者进行一个组合,最合作为一个对象进行创建,新增createGroup()
方法。方法1,客户端维护,不推荐;方法2,注意线程安全,并且注意有的时候因为约束,无法产生新的对象的情况,比如1:2,当两者各创建1个时候,需要等待第二个创建了才能继续创建;方法3: 将约束进行封装,但是需要对原来的方法进行扩展。
3 . 思考
工厂模式中我们举的例子是“无论什么数据类型,都获得一个实例的接口”。抽象工厂模式中,我们提供的是"createProductA"获得具体实例的接口,与其说是抽象,不如说是对于工厂提供实例类别的一种细化!
延伸阅读:、
建造者模式 (Builder Pattern Design)
Separate the construction of a complex object from its representation so taht the same construction process can create different representations.(将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。)
- 建造者模式-通用类图
# Product-class
public class Product{
public void doSomething(){
// 独立业务处理
}
}
# Builder- abstract class
public abstract class Builder{
//设置产品的不同部分
public abstract void setPart();
// 建造产品
public abstract Product buildProduct();
}
# ConcreteBuilder - class
public class ConcreteBuilder extends Builder{
private Product product = new Product();
// 设置产品零件
public void setPart(){
//产品零件业务逻辑
}
// 组件一个产品
public Product buildProduct(){
return product;
}
}
# Director
public class Director{
private Builder builder = new ConcreteBuilder();
// 构造不同的产品
public Product getProduct{
builder.setPart();
// 设置不同的零件,产生不同的产品
return builder.buildeProduct();
}
}
扩展
[Builder类型构造函数-Effective Java 第二条]
原型模式(Prototype Pattern)
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。)
- 原型模式-通用类图
# PrototypeClass - class
class PrototypeClass implements Cloneable{
public PrototypeClass clone(){
PrototypeClass prototypeClass = null;
try{
prototypeClass = (PrototypeClass)super.clone();
}catch(Exception e){
// 异常处理
}
return prototypeClass;
}
}
扩展