概述
工厂模式的作用是封装创建对象的代码,因为创建对象的过程比较复杂,或是对象创建有一些特殊的逻辑,这个时候就可以使用工厂模式来专门生产对象,之所以不在构造函数里做这种逻辑是因为构造函数里的逻辑往往是一些基本初始化动作,这些逻辑不符合构造函数语义,不能放在构造函数里面。
工厂模式分为:简单工厂,工厂方法,抽象工厂。简单工厂虽简单但是用得最多,抽象工厂虽然更抽象更系统化,但是用的很少。工厂模式总的来说就是为了解决实例化多个不同子类的情况,其实是为了方便调用者实例化子类过多的情况,在你通往架构师的道路上,你要有一种感觉,new对象是一种硬编码,尽量不要自己new,要将这个操作让别人或者专门的人来做。
1、简单工厂demo
简单工厂就是通过参数来确定生产的实例类型,或是没有参数,仅仅是封装一段固定的、较长的实例生产代码。简单工厂不满足面向对象开放-关闭原则(对修改关闭,对扩展开放),因为他需要修改。
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
public class ShapeFactory {
// 使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
简单工厂在shiro中使用,实际使用总是看起来难以理解一点,正如现实和理想一样,这里输出的实例有两种类型WebDelegatingSubject和DelegatingSubject两种类型,抓住这个特点看看。
package org.apache.shiro.web.mgt;
import org.apache.shiro.mgt.DefaultSubjectFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.subject.WebSubjectContext;
import org.apache.shiro.web.subject.support.WebDelegatingSubject;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.web.subject.WebSubject;
public class DefaultWebSubjectFactory extends DefaultSubjectFactory {
public DefaultWebSubjectFactory() {
super();
}
public Subject createSubject(SubjectContext context) {
//SHIRO-646
//Check if the existing subject is NOT a WebSubject. If it isn't, then call super.createSubject instead.
//Creating a WebSubject from a non-web Subject will cause the ServletRequest and ServletResponse to be null, which wil fail when creating a session.
boolean isNotBasedOnWebSubject = context.getSubject() != null && !(context.getSubject() instanceof WebSubject);
if (!(context instanceof WebSubjectContext) || isNotBasedOnWebSubject) {
return super.createSubject(context);
}
WebSubjectContext wsc = (WebSubjectContext) context;
SecurityManager securityManager = wsc.resolveSecurityManager();
Session session = wsc.resolveSession();
boolean sessionEnabled = wsc.isSessionCreationEnabled();
PrincipalCollection principals = wsc.resolvePrincipals();
boolean authenticated = wsc.resolveAuthenticated();
String host = wsc.resolveHost();
ServletRequest request = wsc.resolveServletRequest();
ServletResponse response = wsc.resolveServletResponse();
return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,
request, response, securityManager);
}
}
2、工厂方法
生产不同的实例,将生产动作延迟到子类,这里通常产品是一个类型。
工厂方法看到简单工厂的修改毛病,使用了面向对象的常用招式:多态,创建一个接口,定义生产实例的方法,然后多个子类实现这个接口,不同子类做不同的实例化逻辑生产不同的实例,做到用扩展替换了修改,是面向对象编程的一次完美运用。他存在扩展子类过多,代码冗余的缺点。
public interface ObjectFactory {
Object getObject();
}
public class ObjectFactoryOne implements ObjectFactory {
@Override
public Object getObject() {
return null;
}
}
public class ObjectFactoryTwo implements ObjectFactory {
@Override
public Object getObject() {
return null;
}
}
public class FactoryMethodUse {
public static void main(String[] args) {
ObjectFactory objectFactory = new ObjectFactoryOne();
ObjectFactory objectFactory2 = new ObjectFactoryTwo();
objectFactory.getObject();
objectFactory2.getObject();
}
}
3、抽象工厂
抽象工厂的使用场景比上面要复杂一点,因为他要面对的问题是生产的产品类型不同。所以不仅会存在工厂接口,还会存在产品接口。
比如我们现在需要一个文本转Excel和文本转PDF的程序,我们先使用了easy框架作为转换工具。工厂生产不同抽象产品,3个接口永远不发生变化。
public interface SmartFactory {
SmartExcel getExcel();
SmartPdf getPdf();
}
public interface SmartExcel {
void toExcel();
}
public interface SmartPdf {
void toPdf();
}
public class EasyFactory implements SmartFactory{
@Override
public SmartExcel getExcel() {
return null;
}
@Override
public SmartPdf getPdf() {
return null;
}
}
public class EasyPdfImpl implements SmartPdf{
@Override
public void toPdf() {
}
}
public class EasyExcelImpl implements SmartExcel{
@Override
public void toExcel() {
}
}
public class SmartUse {
public static void main(String[] args) {
SmartFactory smartFactory = new EasyFactory();
// 使用
smartFactory.getExcel().toExcel();
smartFactory.getPdf().toPdf();
}
}
如果现在出现了一种更加好用的转化框架fast,我们只需要新增扩展即可,然后将工厂实例替换即可,改动极少。
public class FastFactory implements SmartFactory{
@Override
public SmartExcel getExcel() {
return null;
}
@Override
public SmartPdf getPdf() {
return null;
}
}
public class FastExcelImpl implements SmartExcel{
@Override
public void toExcel() {
}
}
public class FastPdfImpl implements SmartPdf{
@Override
public void toPdf() {
}
}
public class SmartUse {
public static void main(String[] args) {
SmartFactory smartFactory = new EasyFactory();
// 使用
smartFactory.getExcel().toExcel();
smartFactory.getPdf().toPdf();
// 更换框架后使用
SmartFactory smartFactory2 = new FastFactory();
}
}
实际使用案例
jdk中线程工厂接口:java.util.concurrent.ThreadFactory
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}