Java设计模式——简单工厂模式

一、模式定义

简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式(同属于创建型模式的还有工厂方法模式,抽象工厂模式,单例模式,建造者模式)。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
一般由一个工厂类、一个产品基类、多个产品子类构成。由工厂对象决定创建哪种产品类的实例,应用层只需要和工厂类交互。

二、模式结构

在这里插入图片描述
从上图可以看出,简单工厂模式由三部分组成:具体工厂、具体产品和抽象产品。

  • 工厂类(Creator)角色:担任这个角色的是简单工厂模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体Java类实现。

  • 抽象产品(AbstractProduct)角色:担任这个角色的类是由简单工厂模式所创建的对象的父类,或它们共同拥有的接口。抽象产品角色可以用一个Java接口或者Java抽象类实现。

  • 具体产品(ConcreteProduct)角色:简单工厂模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体Java类实现。

三、实例分析

例子参考
这个是Video抽象类,有一个produce()方法:

package com.viagra.design.creational.simpleFactory;
/**
 * Created by viagra
 */
public abstract class Video {
    public abstract void produce();
}

有一个JavaVideo,继承自Video.

package com.viagra.design.creational.simpleFactory;
/**
 * Created by viagra
 */
public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Java课程视频");
    }
}

有一个PythonVideo,继承自Video.

package com.viagra.design.creational.simpleFactory;
/**
 * Created by viagra
 */
public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Python课程视频");
    }
}

测试类:

package com.viagra.design.creational.simpleFactory;

/**
 * Created by viagra
 */
public class Test {
    public static void main(String[] args) {
        Video video = new PythonVideo();
        video.produce();
    }
}

对应的类图如下:

在这里插入图片描述
移植到简单工厂里面:
新建一个工厂类VideoFactory:

ackage com.viagra.design.creational.simpleFactory;

/**
 * @Author: HASEE
 * @Description:
 * @Date: Created in 11:04 2019/4/26
 * @Modified By:
 */
public class VideoFactory  {
	public Video getVideo(String type){
		if ("java".equalsIgnoreCase(type)){
			return new JavaVideo();
		}else if ("python".equalsIgnoreCase(type)){
			return new PythonVideo();
		}
		return null;
	}
}

对应的Test类如下:

package com.viagra.design.creational.simpleFactory;

/**
 * Created by viagra
 */
public class Test {
    public static void main(String[] args) {

    	VideoFactory videoFactory = new VideoFactory();
        Video video = videoFactory.getVideo("java");
        if (video == null){
        	return;
        }
        video.produce();
    }

}

对应的类图如下:
在这里插入图片描述
如果在添加一个算法课程或者前端课程,我们需要修改VideoFactory的代码,而修改这个类就会带来风险,所以就违反了**“开闭原则”
可以通过反射来弥补
简单工厂模式**的局限性:

package com.viagra.design.creational.simpleFactory;
/**
 * @Author: HASEE
 * @Description:
 * @Date: Created in 11:04 2019/4/26
 * @Modified By:
 */
public class VideoFactory  {

	public Video getVideo(Class c){
		Video video = null;
		try {
			video = (Video) Class.forName(c.getName()).newInstance();
		}catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		return video;
	}
	
}

对应Test类的修改:

package com.viagra.design.creational.simpleFactory;

/**
 * Created by viagra
 */
public class Test {
    public static void main(String[] args) {
//	    VideoFactory videoFactory = new VideoFactory();
//	    Video video = videoFactory.getVideo("java");

	    VideoFactory videoFactory = new VideoFactory();
	    Video video = videoFactory.getVideo(JavaVideo.class);
	    if (video == null){
		    return;
	    }
	    video.produce();
    }
}
四、模式优缺点

1.优点

  • 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
    通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
  • 当需要引入新的产品是不需要修改客户端的代码,只需要添加相应的产品类并修改工厂类就可以了,所以说从产品的角度上简单工厂模式是符合“开-闭”原则的。
    2.缺点
  • 由于工厂类集中了所有产品创建逻辑,工厂类一般被我们称作“全能类”或者“上帝类”,因为所有的产品创建他都能完成,这看似是好事,但仔细想想是有问题的。比如全国上下所有的事情都有国家主义一个人干会不会有问题,当然有!一旦不能正常工作,整个系统都要受到影响。
  • 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。所以说从工厂的角度来说简单工厂模式是不符合“开-闭”原则的。
    简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
五、在JDK源码中的使用
  • 1.Java中的Calendar类,在创建Calendar实例时,就使用了简单工厂。
private static Calendar createCalendar(TimeZone zone, Locale aLocale) {
    ... ...
	Calendar cal = null;
    if (aLocale.hasExtensions()) {
	    String caltype = aLocale.getUnicodeLocaleType("ca");
	    if (caltype != null) {
	    	switch (caltype) {
	        case "buddhist":
	        	cal = new BuddhistCalendar(zone, aLocale);
	        	break;
	        case "japanese":
	            cal = new JapaneseImperialCalendar(zone, aLocale);
	            break;
	        case "gregory":
	        	cal = new GregorianCalendar(zone, aLocale);
	        	break;
			}
		}
	}
	if (cal == null) {
		if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
			cal = new BuddhistCalendar(zone, aLocale);
		} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") {
			cal = new JapaneseImperialCalendar(zone, aLocale);
		} else {
			cal = new GregorianCalendar(zone, aLocale);
		}
	}
	return cal;
}

Calendar的UML图如下,
在这里插入图片描述
有三个子类BuddhistCalendar,JapaneseImperialCalendar和GregorianCalendar。所以使用者在创建Calendar时,并不需要特别创建某一具体的Calendar,工厂会根据其时区等属性,自动生产合适的Calendar对象。

  • 2.jdk中的线程池:ThreadPoolExecutor,根据自己的需求传入corePoolSize、maximumPoolSize、keepAliveTimem、keepAliveTimem、unit、threadFactory、handler这几个参数,new一个指定的ThreadPoolExecutor出来。
      jdk提供了Executors这个类,让开发者对线程池的使用与生产分离开,开发者只需要调用不同的方法就可以获取到不同的线程池,开发者不用关心线程池的实现细节,只需要调用api即可获取不同的线程池。
       如:Executors.newSingleThreadExecutor() 获取单线程的线程池
       Executors.newCachedThreadPool() 获取无界线程池
  • 3.slf4j 中简单方法的使用
  • 在这里插入图片描述
package org.slf4j;
public final class LoggerFactory {
//..
	public static Logger getLogger(Class clazz) {
    	return getLogger(clazz.getName());
 	}
 	public static Logger getLogger(String name) {
    	ILoggerFactory iLoggerFactory = getILoggerFactory();
    	return iLoggerFactory.getLogger(name);
  }
//..
}

查看IloggerFactory类:

package org.slf4j;
// 工厂方法
public interface ILoggerFactory {

    public Logger getLogger(String name);
}

在这里插入图片描述
打开第一个类看看:

package ch.qos.logback.classic;
//..
public class LoggerContext extends ContextBase implements ILoggerFactory,
        LifeCycle {
	public final Logger getLogger(final String name) {

		if (name == null) {
			throw new IllegalArgumentException("name argument cannot be null");
		}

		// if we are asking for the root logger, then let us return it without
		// wasting time
		if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {// 返回root节点的logger
			return root;
		}

		int i = 0;
		Logger logger = root;

		// check if the desired logger exists, if it does, return it
		// without further ado.
		Logger childLogger = (Logger) loggerCache.get(name);
		// if we have the child, then let us return it without wasting time
		if (childLogger != null) {// 返回子节点的logger
			return childLogger;
		}

		// if the desired logger does not exist, them create all the loggers
		// in between as well (if they don't already exist)
		String childName;
		while (true) {
			int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
			if (h == -1) {
				childName = name;
			} else {
				childName = name.substring(0, h);
			}
			// move i left of the last point
			i = h + 1;
			synchronized (logger) {
				childLogger = logger.getChildByName(childName);
				if (childLogger == null) {
					childLogger = logger.createChildByName(childName);
					loggerCache.put(childName, childLogger);
					incSize();
				}
			}
			logger = childLogger;
			if (h == -1) {
				return childLogger;
			}
		}
	}
}

这里用了工厂方法模式,和简单工厂模式,这是一个组合模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值