前言:从菜鸟教程学习整理,有兴趣的的可以去系统了解一下。
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
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;
}
}
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
接下来优化:
枚举优化:
增加枚举类
public enum ShapeType{
CIRCLE,
RECTANGLE,
SQUARE
}
修改工厂类的工厂方法,个人建议工厂方法应该是静态方法或者采用单例模式:
public class ShapeFactory{
public static Shape getShape(ShapeType type){
switch(type){
case CIRCLE:
return new Circle();
case RECTANGLE:
return new Rectangle();
case SQUARE:
return new Square();
default:
throw new UnknownTypeException();
}
}
}
最后是使用示例:
public class FactoryPatternDemo {
public static void main(String[] args) {
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = ShapeFactory.getShape(ShapeType.CIRCLE);
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = ShapeFactory.getShape(ShapeType.RECTANGLE);
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = ShapeFactory.getShape(ShapeType.SQUARE);
//调用 Square 的 draw 方法
shape3.draw();
}
}
附:
先看一下工厂模式是用来干什么的——属于创建模式,解决子类创建问题的。换句话来说,调用者并不知道运行时真正的类名,只知道从“Circle"可以创建出一个shape接口的类,至于类的名称是否叫'Circle",调用者并不知情。所以真正的对工厂进行扩展的方式(防止程序员调用出错)可以考虑使用一个枚举类(防止传入参数时,把circle拼写错误)。如果调用者参肯定类型是Circle的话,那么其工厂没有存在的意义了!
所以说下边所谓的优化是不存在的,一定要搞清楚,工厂模式是用来创建对象的,client并不知道需要创建哪一个类,他只有一个参数标志,就是那个字符串,但是字符串并不一定有什么联系。
其实在.net类库中存在这个模式的的一个典型的。但他引入的另一个概念“可插入编程协议”。
那个就是WebRequest req = WebRequest.Create("http://ccc......");可以自动创建一个HttpWebRequest的对象,当然,如果你给定的是一个ftp地址,他会自动创建一个FtpWebRequest对象。工厂模式中着重介绍的是这种通过某个特定的参数,让你一个接口去干对应不同的事而已!而不是调用者知道了类!
但是可取的是我们可以学一下下边的反射加范型的运用可以节省不少代码呢,不够当然和工厂模式搭不上什么边了。
public class ShapeFactory {
public static <T> T getClass(Class<? extends T> clazz) {
T obj = null;
try {
obj = (T) Class.forName(clazz.getName()).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return obj;
}
}
//省略类型强制转换,支持多态
Rectangle rect = ShapeFactory.getClass(Rectangle.class);
rect.draw();
Shape square = ShapeFactory.getClass(Square.class);
square.draw();
在jdk9中直接使用泛型的newInstance方法已经过时。重写的getClass()方法如下:
public <T> T getClass(Class<? extends T> clazz) {
T obj = null;
try {
obj = clazz.getDeclaredConstructor().newInstance();
}
catch (ReflectiveOperationException e) {
e.printStackTrace();
}
return obj;
}