工厂模式其实就是new一个对象,什么情况下应该考虑用工厂模式呢?
- new一个对象的时候要做很多的初始化工作,全部写在构造器里面会导致代码很臃肿.
- 当很多地方要new这个对象的时候,如果很多地方new了这个对象,突然要改一点,这时候如果用了工厂方法就会很庆幸了.
- 当客户端想要new 一个产品,但是这个类似产品很多(比如100个),构造很复杂,客户端压根就不想知道用哪个,怎么构造,反正有个可以用就行,这时客户端如果能根据少数几个参数就能得到想要的产品那就感觉很方便了.
所以这时工厂干的事就是根据少数几个参数通过一定的逻辑确定返回哪个产品.
工厂模式是设计模式中最基础的,也是比较常用的设计模式,大致可分为3种.
1. 简单工厂模式
2. 工厂方法模式
3. 抽象工厂模式
先分析下简单工厂模式,先上类图有个宏观的了解.
代码如下:
- 先定义一个产品接口
/**
* FileName :Shape
* Author :zengzhijun
* Date : 2018/5/18 14:34
* Description:
*/
package com.byedbl.shape;
/**
* 抽象方法
* 定义形状的抽象
* @author : zengzhijun
* @date : 2018/5/18 14:34
**/
public interface Shape {
void draw();
void erase();
}
- 再有两个实现类,分别为Circle 和 Square
代码如下:
/**
* FileName :Circle
* Author :zengzhijun
* Date : 2018/5/18 14:38
* Description:
*/
package com.byedbl.shape;
/**
* Shape 抽象的一个实现
* @author : zengzhijun
* @date : 2018/5/18 14:38
**/
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Circle draw");
}
@Override
public void erase() {
System.out.println("Circle erase");
}
}
/**
* FileName :Square
* Author :zengzhijun
* Date : 2018/5/18 14:36
* Description:
*/
package com.byedbl.shape;
/**
* 抽象的一个实现
* @author : zengzhijun
* @date : 2018/5/18 14:36
**/
public class Square implements Shape{
@Override
public void draw() {
System.out.println("Square draw");
}
@Override
public void erase() {
System.out.println("Square erase");
}
}
- 然后定义一个工厂类负责创建
/**
* FileName :ShapeStaticFactory
* Author :zengzhijun
* Date : 2018/5/18 14:40
* Description:
*/
package com.byedbl.shape.factory.simple;
import com.byedbl.shape.Circle;
import com.byedbl.shape.Shape;
import com.byedbl.shape.Square;
/**
* 创建形状的静态工厂
* @author : zengzhijun
* @date : 2018/5/18 14:40
**/
public class ShapeStaticFactory {
public static Shape create(int no) {
//一般会改成反射来创建
Shape shape ;
switch (no){
case 1:
shape = new Square();
break;
case 2:
shape = new Circle();
break;
default:
shape = new Square();
break;
}
return shape;
}
}
- 客户端调用,代码如下:
package com.byedbl.shape;
import com.byedbl.shape.factory.simple.ShapeStaticFactory;
import org.junit.Test;
/**
* <pre>
* 本例为静态工厂方法(简单工厂模式)
* 工厂方法能增加系统的扩展性,实现可插拔的效果
*
* 工厂模式其实就相当于创建实例对象的new
*
* 当有新的产品加入时,我们需要增加一个产品类继承自 {@link Shape} ,
* 并且在 {@link ShapeStaticFactory} 中修改 {@link ShapeStaticFactory} 的 create 方法
* 因为要修改工厂方法的代码,违反了开闭原则,这就是这个模式的缺点.而工厂方法模式就避免了这一点
* </pre>
* @author : zengzhijun
* @date : 2018/5/18 14:48
**/
public class ShapeStaticFactoryTest {
@Test
public void create() {
Shape shape = ShapeStaticFactory.create(1);
shape.draw();
shape.erase();
System.out.println("----------------------");
shape = ShapeStaticFactory.create(2);
shape.draw();
shape.erase();
}
}
- 小结
工厂模式 提高了程序的扩展性.
简单工厂模式和其他两种工厂模式的主要区别在于工厂,简单工厂模式中没有对工厂抽象,直接在工厂中创建具体的对象.并且一般是用静态方法创建
该方法有个缺点,工厂违反了开闭原则,在需要新增一个产品的时候需要修改工厂类,工厂方法模式可以避免这一点
-
工厂方法模式
先上类图:
工厂方法模式比上面的简单工厂模式多了3个类,分别是抽象工厂Factory
,两个工厂实现类CircleFactory 和 SquareFactory
代码如下:
/**
* FileName :Factory
* Author :zengzhijun
* Date : 2018/5/18 15:14
* Description:
*/
package com.byedbl.shape.factory.method;
import com.byedbl.shape.Shape;
/**
* 定义抽象工厂方法
* @author : zengzhijun
* @date : 2018/5/18 15:14
**/
public interface Factory {
Shape create();
void eat();
}
/**
* FileName :CircleFactory
* Author :zengzhijun
* Date : 2018/5/18 15:16
* Description:
*/
package com.byedbl.shape.factory.method;
import com.byedbl.shape.Circle;
import com.byedbl.shape.Shape;
public class CircleFactory implements Factory {
@Override
public Shape create() {
return new Circle();
}
@Override
public void eat() {
System.out.println("Circle eat");
}
}
/**
* FileName :SquareFactory
* Author :zengzhijun
* Date : 2018/5/18 15:15
* Description:
*/
package com.byedbl.shape.factory.method;
import com.byedbl.shape.Shape;
import com.byedbl.shape.Square;
public class SquareFactory implements Factory{
@Override
public Shape create() {
return new Square();
}
@Override
public void eat() {
System.out.println("Square eat");
}
}
客户端调用的代码如下:
package com.byedbl.shape.factory.method;
import com.byedbl.shape.Shape;
import org.junit.Test;
import org.springframework.util.ClassUtils;
/**
* <pre>
* 工厂方法模式的实现类
* 工厂方法模式是在简单工厂的基础上将工厂也抽象出一个抽象类
* 这样创建的工厂也实现了可插拔的效果
* 同时也解决了简单工厂在增加产品时要修改工厂类的缺点
*
* 现在如果要增加一个产品,只需增加一个 {@link Shape} 接口的实现 和一个 {@link Factory}接口的实现
*
* 更进一步的,我们可以将 new CircleFactory() 这个过程改成反射获取,
* 从而做到客户端无需任何修改,只要改环境配置就达到创建不同产品的效果
*
* @author : zengzhijun
* @date : 2018/5/18 15:20
**/
public class FactoryTest {
@Test
public void create() {
Factory factory = new CircleFactory();
Shape shape = factory.create();
shape.draw();
shape.erase();
factory.eat();
System.out.println("--------------");
factory = new SquareFactory();
shape = factory.create();
shape.draw();
shape.erase();
factory.eat();
}
@Test
public void createDynamic() throws ReflectiveOperationException {
//这个值可以配置到外部文件中
String factoryName = CircleFactory.class.getCanonicalName();
// factoryName = SquareFactory.class.getCanonicalName();
Class<?> clazz = ClassUtils.forName(factoryName,ClassUtils.getDefaultClassLoader());
Factory factory = (Factory) clazz.newInstance();
Shape shape = factory.create();
shape.draw();
shape.erase();
factory.eat();
}
}
- 小结
工厂方法模式是在简单工厂的基础上将工厂也抽象出一个抽象类
更进一步的,我们可以将 new CircleFactory() 这个过程改成反射获取,
从而做到客户端无需任何修改,只要改环境配置就达到创建不同产品的效果
再进一步,实际的应用中我们可能会直接用一个可动态创建不同类的工厂,这样就不用这么多工厂类了,但是这样类图就看起来是简单工厂模式了,只是这个工厂很强大,一个顶n个。
下面分析 抽象工厂模式
还是先贴类图:
简单说下抽象工厂模式解决的问题,上面这个类图据说是该模式创建的场景。
假设不同操作系统下都有Button和Text控件,比如,Unix和Windows。
Text有两个实现分别是WindowsText和UnixText,同理Button也有两个不同的实现,如果按照上面工厂方法的套路,我们应该给Button和Text分别创建一个抽象工厂接口ButtonFactory和TextFacTory
,以及不同环境的ButtonWindowsFactory,ButtonUnitFactory,TextWindowsFactory和 TextUnitFactory
。这样工厂的类就有6个,
但是一般来讲我们在一个操作系统环境里面是只会有一套Button和Text,我们就会想可不可以把6个Factory合并呢?合并一下就诞生了 抽象工厂模式
代码如下:
- 创建 Button 和 Text 产品接口
/**
* FileName :Button
* Author :zengzhijun
* Date : 2018/5/18 16:24
* Description:
*/
package com.byedbl.system;
/**
* 定义一个产品
* 操作系统中的一个按钮
* @author : zengzhijun
* @date : 2018/5/18 16:24
**/
public interface Button {
void show();
}
/**
* FileName :Text
* Author :zengzhijun
* Date : 2018/5/18 16:27
* Description:
*/
package com.byedbl.system;
/**
* 再定义一个产品
* 系统的文本框
* @author : zengzhijun
* @date : 2018/5/18 16:27
**/
public interface Text {
void display();
}
- 创建不同环境的Button和Text实现类
/**
* FileName :UnixButton
* Author :zengzhijun
* Date : 2018/5/18 16:25
* Description:
*/
package com.byedbl.system;
/**
* 具体的产品
* Unix系统的按钮
* @author : zengzhijun
* @date : 2018/5/18 16:25
**/
public class UnixButton implements Button{
@Override
public void show() {
System.out.println("UnixButton show");
}
}
/**
* FileName :WindowsButton
* Author :zengzhijun
* Date : 2018/5/18 16:26
* Description:
*/
package com.byedbl.system;
/**
* 定义一个具体的产品
* Windows操作系统的按钮
* @author : zengzhijun
* @date : 2018/5/18 16:26
**/
public class WindowsButton implements Button{
@Override
public void show() {
System.out.println("WindowsButton show");
}
}
/**
* FileName :WindowsButton
* Author :zengzhijun
* Date : 2018/5/18 16:26
* Description:
*/
package com.byedbl.system;
/**
* 定义一个具体的产品
* Windows操作系统的按钮
* @author : zengzhijun
* @date : 2018/5/18 16:26
**/
public class WindowsButton implements Button{
@Override
public void show() {
System.out.println("WindowsButton show");
}
}
/**
* FileName :WindowsText
* Author :zengzhijun
* Date : 2018/5/18 16:29
* Description:
*/
package com.byedbl.system;
public class WindowsText implements Text {
@Override
public void display() {
System.out.println("WindowsText display");
}
}
- 创建工厂接口和工厂实现类
/**
* FileName :Factory
* Author :zengzhijun
* Date : 2018/5/18 16:29
* Description:
*/
package com.byedbl.system;
/**
* 定义工厂的抽象
* 定义创建不同操作系统的按钮和文本框
* @author : zengzhijun
* @date : 2018/5/18 16:29
**/
public interface Factory {
Button createButton();
Text createText();
}
/**
* FileName :UnixFactory
* Author :zengzhijun
* Date : 2018/5/18 16:32
* Description:
*/
package com.byedbl.system;
public class UnixFactory implements Factory {
@Override
public Button createButton() {
return new UnixButton();
}
@Override
public Text createText() {
return new UnixText();
}
}
/**
* FileName :WindowsFactory
* Author :zengzhijun
* Date : 2018/5/18 16:33
* Description:
*/
package com.byedbl.system;
public class WindowsFactory implements Factory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public Text createText() {
return new WindowsText();
}
}
4 . 客户端调用
package com.byedbl.system;
import org.junit.Test;
/**
* <pre>
* 抽象工厂模式
* 抽象工厂模式解决的问题是:
* </pre>
* <ol>
* <li> 系统中有多于一个的产品族,比如既有 {@link Button}又有 {@link Text}
* <li> 系统只消费其中某一族产品,比如 Windows 只消费 {@link WindowsButton} 和 {@link WindowsText}
* 这个是一个很重要的条件,如果不是某一族,比如可能同时消费 {@link WindowsButton} 和 {@link UnixButton} ,这个时候抽象工厂也没辙了,只能用工厂方法了.
* </ol>
* <pre>
* 如果不用抽象工厂方法,而用工厂方法,那么 Button 就要对应有 ButtonFactory 工厂接口,下有
* WindowsButtonFactory 和 UnixButtonFactory 两个工厂实现类,同理 Text 也要有 TextFactory
* 工厂接口,下有WindowTextFactory 和 UnixTextFactory 两个工厂实现类;
*
* 而根据上面的前提2,消费者只能消费其中一族产品,那么我们就没必要创建这么多工厂了, WindowsButtonFactory 和
*WindowTextFactory 合并成 WindowFactory; UnixButtonFactory 和 UnixTextFactory 合并成
* UnixFactory ;这样就省了一组工厂类,而产品越多,省得工厂类就更多,一套一套的省.
*
*
* 更进一步:
* <ol>
* <li> 如果Factory 动态反射获取,那么客户端就可以完全不修改而启用的产品族
* <li> 如果 DynamicFactory 可以利用反射和配置动态创建不同环境的 {@link Button} 和 {@link Text},那么就不用 {@link WindowsFactory} 和 {@link UnixFactory} 了,这样就可以解决消费者在Windows情况下要
* UnixText的情况了,但是还是不能解决在Windows情况下既有{@link WindowsButton}又有 {@link UnixButton} 的问题,所以说
* 设计模式都是解决特定问题的方案.
* </pre>
* @author : zengzhijun
* @date : 2018/5/18 16:36
**/
public class FactoryTest {
@Test
public void create() {
Factory factory = new WindowsFactory();
Button button = factory.createButton();
Text text = factory.createText();
button.show();
text.display();
System.out.println("-----------");
factory = new UnixFactory();
button =factory.createButton();
text = factory.createText();
button.show();
text.display();
}
}
- 小结
抽象工厂方法之所以能将本来要两套的工厂合并是因为一个前提:
消费者一般只会消费其中一组产品。
-
工厂方法在实例化泛型中的应用
我们都知道 Java 的泛型是采用类型擦除来实现的(在 javac 编译过程的中把泛型去掉,加上强制类型转换)。所以我们不能直接 new T()来实例化一个对象。其实可以采用工厂方法模式设计模式来解决。假设我们有一个类,里面要用到了泛型的实例化。
public class Foo<T>(){
private T t;
public Foo(){
t = new T(); // 这个代码是有问题的,我们使用工厂设计模式进行改进
}
}
我们给出工厂接口如下:
public interface IFactory<T>(){
T create();
}
进而我们可以采用如下的方法进行改进
public class Foo<T>(){
private T t;
public <F extends IFactory<T>> Foo(F factory){
// t = new T();
factory.create();
}
}
这个时候,我们可以采用如下的方式实例化 Foo
new Foo(new Ifactory<Integer>(){
Integer create(){
return new Integer(0);
}
});
new Foo(new Ifactory<String>(){
String create(){
return "Hello";
}
});