设计模式之(二) 创建者模式

版权声明:欢迎转载,转载请说明出处https://csdn.yanxml.com。大数据Github项目地址https://github.com/SeanYanxml/bigdata。 https://blog.csdn.net/u010416101/article/details/84885736

前言

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


  • 优点 & 缺点 & 使用场景

优点:

  1. 良好的封装性,代码结构清晰。(调用某个产品类,只需要知道产品的类名即可。)
  2. 工厂模式的方法扩展性非常好。(只需要简单更改具体的工厂类,或扩展一个工厂类即可实现变化。比如新的产品类,本工厂几乎不需要更改。)
  3. 屏蔽产品类。(JDBC->MySQL切换Oracle 只需要更改名称。具体实现不需要知晓。)
  4. 典型的解耦框架。(符合 迪米特法则/依赖倒置原则/里氏替换原则)

缺点: 部分场景可能不需要。过度设计,增加复杂度。

使用场景:

  1. new Product()的替代品。需要根据产品规模决定是否需要使用工厂模式。
  2. 需要灵活的、可扩展的框架时,可以考虑工厂方法。(比如邮箱服务 提供POP3/IMAP/HTTP协议服务。增加新的服务协议,只需要实例化新协议即可。)
  3. 异构项目中。(跨平台文件(Java-Python) 当作产品类。)
  4. 测试驱动框架中,实例化生成某些依赖的对象。(因JMoke的出现弱化。)
  • 扩展
  1. 扩展1-缩小为简单工厂模式
  2. 扩展2-抽象工厂模式
  3. 扩展3-替代Singleton单例模式
  4. 扩展4-延迟加载工厂类
  • 扩展1-缩小为简单工厂模式

工厂模式-扩展1(简单工厂模式)

  • 扩展2-升级多个工厂类

扩展2-抽象工厂模式

  • 扩展3-替代Singleton单例模式

扩展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)

扩展4-延迟初始化

# 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.(为创建一组相关或相互依赖的对象提供一个接口,而且无须指定他们的具体类。)

  • 抽象工厂模式-通用类图

抽象工厂模式-通用类图

抽象工厂模式-通用类图2

#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
    另: 书中提及的男女工厂,实在没有太多的意思,这边就将类图绘制于此。

抽象工厂模式用例1-女娲造人(男/女)

  • 优点 & 缺点 & 使用场景

优点:

  1. 封装性,与抽象工厂一致。只需要知道产品的名字即可通过工厂创建一个对象。
  2. 产品族内约束非公开。即,可以在产品创建的时候以一定比例进行约束。(比如A和B产品以1:2的比例进行约束,在实现类Creator1中约束即可。)

缺点:

  1. 缺点非常明显,扩展性差。比如需要增加产品线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 第二条]

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

扩展

还有这种操作?(一)

重写BeanUtils.copyProperties(复制对象属性方法)

BeanUtils copyproperties()集合属性可以复制吗?

猜你喜欢

转载自blog.csdn.net/u010416101/article/details/84885736