设计模式二–工厂模式
举例:
女娲采集黄土捏成人的形状,然后放到八卦炉中烧制,然后放到土地上生长,但是意外随时都会发生。
- 第一次烤人,感觉应该熟了,往地上一放,哇,没烤熟,白人诞生了。
- 第二次烤人,上一次没有烤熟,这次多烤一会,放到世间一看,哇,熟过头了,于是黑人诞生了。
- 第三次烤人,一边烧制一遍看,知道表皮发黄,嘿,刚刚好,于是黄种人诞生了。
思考:
- 在面向对象编程的思想中,万物皆对象。于是我们抽象出具体的三个对象。
- 女娲,八卦炉,三种不同肤色的人。
- 女娲可以用场景类Client来表示,八卦炉可以用工厂来表示,三个不同肤色的人,他们都是同一个接口下面的不同实现类。
于是我们生成类图。
- AbstarctHummanFactory是一个抽象类,HumanFactoruy是一个实现类,完成具体的任务,创建人类。Human是人类- 总称,三个人种是其实现类,Nawa是一个场景,负责模拟这个场景,发起任务。
人类接口
/**
* @atuhor sha1024
*/
public interface Human {
void getTalk();
void getColor();
}
白人人类
/**
* @atuhor sha1024
*/
public class WhiteHuman implements Human{
@Override
public void getTalk() {
System.out.println("我是一个白人");
}
@Override
public void getColor() {
System.out.println("我的皮肤是白色的");
}
}
黄色人种
/**
* @atuhor sha1024
*/
public class YellowHuman implements Human{
@Override
public void getTalk() {
System.out.println("黄种人说话最好听");
}
@Override
public void getColor() {
System.out.println("我的皮肤是黄色的");
}
}
黑色人种
/**
* @atuhor sha1024
*/
public class BlackHuman implements Human{
@Override
public void getTalk() {
System.out.println("黑人说话听不懂");
}
@Override
public void getColor() {
System.out.println("我的皮肤是黑色的");
}
}
定义一个抽象工厂,定义了一个泛型实现了Human
/**
* @atuhor sha1024
*/
public abstract class AbstarctHummanFactory {
public abstract <T extends Human> T createHuman(Class<T> c);
}
定义了一个人类工厂实现抽象人类工厂
public class HummanFactory extends AbstarctHummanFactory {
@Override
public <T extends Human> T createHuman(Class<T> c) {
//定义一个人种
Human human = null;
try{
human = (T)Class.forName(c.getName()).newInstance();
}catch (Exception e){
throw new RuntimeException("定义人种错误");
}
return (T)human;
}
}
女娲类,实现具体的场景
public static void main(String[] args) {
AbstarctHummanFactory hummanFactory = new HummanFactory();
Human whiteHuman = hummanFactory.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.getTalk();
Human blackHuman = hummanFactory.createHuman(BlackHuman.class);
blackHuman.getTalk();
blackHuman.getColor();
Human yellowHuman = hummanFactory.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.getTalk();
SpringApplication.run(FactoryApplication.class, args);
}
执行结果
工厂方法模式的定义
- 定义一个用于创建对象的接口,让子类来决定实例化哪一个。工厂方法使一个类的实例化延迟到其子类。
在工厂模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象的定义;Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreateCreator完成的。
具体的产品类可以有多个,都继承抽象产品类
/**
* 抽象产品类
* @atuhor sha1024
*/
public abstract class Product {
//产品类的公共方法
public void method1(){
System.out.println("我是抽象工厂方法1");
}
/**
* 抽象方法
*/
public abstract void method2();
}
产品类1
/**
* @atuhor sha1024
*/
public class ConcreateProduct1 extends Product {
@Override
public void method2() {
/**
* 具体业务逻辑
*/
}
}
产品类2
/**
* @atuhor sha1024
*/
public class ConcreateProduct2 extends Product {
@Override
public void method2() {
/**
* 业务逻辑
*/
}
}
抽象工厂
/**
* @atuhor sha1024
*/
public abstract class Creator {
/**
* 创建一个产品对象,其输入任何参数类型都可以自行设置
* 通常为String,Enum,Class,也可以为空
*/
public abstract <T extends Product> T createProduct(Class<T> c);
}
具体如何产生一个产品对 对象是由具体的工厂实现类来决定的
/**
* 通过发射获取到实例
* @atuhor sha1024
*/
public class ConcreateCreator extends Creator{
@Override
public <T extends Product> T createProduct(Class<T> c) {
Product product = null;
try{
product = (T)Class.forName(c.getName()).newInstance();
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
return (T)product;
}
}
场景类
/**
* @atuhor sha1024
*/
public class Client {
public static void main(String[] args){
Creator creator = new ConcreateCreator();
Product product = creator.createProduct(ConcreateProduct1.class);
/**
* 业务逻辑
*/
product.method2();
}
}
工厂模式的优点:
- 良好的封装性,代码结果清晰。
- 工厂方法模式的扩展性非常好。
- 屏蔽产品类,产品类如何变化,调用者不用关心,它只需要关心产品的接口。更好的实现了解耦合。
工厂方法模式使用场景
- 工厂方法是new一个对象的 替代品,所以在所有需要生成对象的地方都可以使用,但是要考虑是否要增加一个产品类来增加代码的复杂性。
- 需要灵活的可扩展的框架时,可以考虑采用工厂方法模式。
工厂模式的扩展
缩小为简单工厂模式
上图中我们去掉了AbstarctHummanFactory
抽象人类工厂,同时把createHuman改为静态类型。
/**
* @atuhor sha1024
*/
public class NvWa {
public static void main(String[] args){
Human human = HummanFactory2.createHuman(YellowHuman.class);
Human human2 = HummanFactory2.createHuman(BlackHuman.class);
}
}
运行结果没用发生变化,但是我们的类图变得更加简单了,因为简单所以我们称为简单工厂模式
升级为多个工厂模式
当我们在做一个复杂项目的时候,经常会遇到初始化一个对象很费力的情况,所有产品都放到一个工厂方法中进行初始化会使代吗结构不清晰。例如:一个产品类有5个具体实现类,每个实现类的初始化方法都不相同,如果写在工厂中,会导致该方法巨大无比。
考虑到要结构清晰,我们就为每个产品定义一个创造者,然后由调用者去选择与哪个工厂方法关联。
定义一个抽象工厂
public abstract class AbstarctHummanFactory {
public abstract Human createHuman();
}
黑人人种创建工厂
public class BlackHummanFactory extends AbstarctHummanFactory {
@Override
public Human createHuman() {
return new BlackHuman();
}
}
黄色人种创建工厂
public class YellowHummanFactory extends AbstarctHummanFactory {
@Override
public Human createHuman() {
return new YellowHuman();
}
}
场景类
public static void main(String[] args){
Human yellowHuman = (new YellowHummanFactory()).createHuman();
yellowHuman.getTalk();
yellowHuman.getColor();
Human blackHuman = (new BlackHummanFactory()).createHuman();
blackHuman.getTalk();
blackHuman.getColor();
}
运行结果还是相同的,每一个产品类都对应了一个创建类好处就是创建类职责清晰,而且结构简单,但是给可扩展性和维护性带来了一定的影响。
当然,在复杂的应用场景中一般采用多工厂模式,然后增加一个协调类,避免调用者和各个子工厂交流,对高层模块提供统一调用api。
替代单例模式
Singleton定义了一个private 的无参构造函数,目的是不允许通过new 的方式创建一个对象
public class Singleton {
private Singleton(){}
public void doSomeThing(){
//业务逻辑
}
}
单例类,Singleton保证不能通过正常情况下建立一个对象,只能通过反射方式创建
public class SingletonFactory {
private 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;
}
通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内部中的对象唯一。当然,其他的类也能通过反射的方式获得一个单例对象。
延迟初始化
ProductFactory负责产品类对象的创建,并通过prMap创建一个缓存,对需要被重用的对象保留
public class ProductFactory {
private static final Map<String,Product> prMap = new HashMap<>();
public static synchronized Product createProduct(String type) throws Exception{
Product product = null;
//判断缓存中是否有这个对象
if(prMap.containsKey(type)){
product = prMap.get(type);
}else{
if(type.equals("Product1")){
product = new ConcreateProduct1();
}else{
product = new ConcreateProduct2();
}
//同时缓存数据到prMap中
prMap.put(type,product);
}
return product;
}
}
通过定义一个Map容器,容纳所有对象,如果Map容器中已经有的对象,则直接取出;如果没用,则根据需要的类型产生一个对象并放入到Map容器中。