简单工厂模式
3 简单工厂模式 (Simple Factory Pattern)
3.1 动机与定义
它不属于设计模式之一,但是非常简单常用,因此在此处介绍。
简单工厂模式
:又称为静态工厂方法(Static Factory Method)模式
,属于创建型模式
。==简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。==它定义了一个创建对象的类
,由这个类来封装实例化对象行为
。
3.2 结构与角色
简单工厂模式的实质
是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类的实例,这些产品类继承自一个父类或接口。
简单工厂模式包含三个角色。
①Factory:工厂。简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
②Product:抽象产品。简单工厂模式所创建的所有对象的父类型,他负责描述所有实例所共用的接口。
③Concrete Product:具体产品。简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
3.3 实例分析
这里有一个披萨店,店里有两种披萨种类:GreekPizza、CheesePizza。披萨经过制作以后,你作为前台需要完成披萨店的订购功能。当有客人进行点单的时候,你需要确认订单通知制作披萨。接下来我们将用代码去一步步实现。
首先是Product抽象产品的构建:
package SimpleFactoryPattern.PizzaStore.pizza;
//将Pizza类做成抽象
public abstract class Pizza {
//披萨名字
private String name;
//不同的披萨所用到的配料不同,制作方法也不相同
//所以把它设计成抽象方法,有不同的披萨子类去实现这个方法
public abstract void prepare();
//其余方法 都大同小异,所以在父类中直接写出就好
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name +"boxing");
}
public void setName(String name) {
this.name = name;
}
}
Concrete Product:具体产品。我们的两种披萨,继承披萨父类,并实现prepare方法。
package SimpleFactoryPattern.PizzaStore.pizza;
public class CheesePizza extends Pizza{
@Override
public void prepare() {
// TODO Auto-generated method stub
System.out.println("奶酪披萨材料准备好了!");
}
}
package SimpleFactoryPattern.PizzaStore.pizza;
public class GreekPizza extends Pizza{
@Override
public void prepare() {
// TODO Auto-generated method stub
System.out.println("希腊披萨材料准备好了!");
}
}
接下来就是订购披萨的操作了。这相当于是一个顾客对应的一个订单。
package SimpleFactoryPattern.PizzaStore.Order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import SimpleFactoryPattern.PizzaStore.pizza.CheesePizza;
import SimpleFactoryPattern.PizzaStore.pizza.GreekPizza;
import SimpleFactoryPattern.PizzaStore.pizza.Pizza;
public class OrderPizza {
public OrderPizza() {
Pizza pizza = null;
String orderType; //订购披萨类型
do {
//进入选择系统,匹配披萨种类
orderType = getType();
if(orderType.equals("greek")) {
//创建不同的披萨对象
pizza = new GreekPizza();
pizza.setName("希腊披萨");
}else if(orderType.equals("cheese")){
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}else {
break;
}
//输出披萨制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
private String getType() {
try {
//顾客告诉前台,要什么口味的披萨,前台在系统中选择
BufferedReader bfReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String string;
string = bfReader.readLine();
return string;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
}
客户端代码:
package SimpleFactoryPattern.PizzaStore.Order;
//相当于一个客户端,发出订购任务
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza();
}
}
/*
运行结果:
input pizza type:
greek
希腊披萨材料准备好了!
希腊披萨baking
希腊披萨cutting
希腊披萨boxing
input pizza type:
cheese
奶酪披萨材料准备好了!
奶酪披萨baking
奶酪披萨cutting
奶酪披萨boxing
input pizza type:
xxx
*/
由上述代码我们可以观察到:
他违反了设计模式的ocp原则,即对扩展开放,对修改关闭。当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。就上述代码而言:如果在其他地方也有创建Pizza对象的代码,我们便需要对这些地方都进行修改。直接new该对象会对该对象耦合严重,更换对象,所有new对象的地方都需要修改一遍,因此就违背了我们软件设计的开闭原则。
再通俗易懂的说就是,上面的OrderPizza类就类似于,每个顾客点单后产生的一个订单,每一个订单中都需要去new对象,如果新增加了pizza种类,每一个订单中OrderPizza() 这个方法都需要进行修改,订单一多那简直是麻烦透顶了!根本就是给人找事儿!!!
那么如何改进这段代码呐?
把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类就可以了,
其他有创建到Pizza对象的代码就不需要修改了,这就是简单工厂模式。
至此我们引入简单工厂,让工厂去给我们制作披萨,增加SimpleFactory类:
package SimpleFactoryPattern.PizzaStore.Order;
//简单工厂类
import SimpleFactoryPattern.PizzaStore.pizza.CheesePizza;
import SimpleFactoryPattern.PizzaStore.pizza.GreekPizza;
import SimpleFactoryPattern.PizzaStore.pizza.Pizza;
public class SimpleFactory {
//根据orderType 返回对应的Pizza对象
public Pizza creatPizza(String orderType){
Pizza pizza = null;
System.out.println("使用简单工厂模式");
if(orderType.equals("greek")) {
//创建不同的披萨对象
pizza = new GreekPizza();
pizza.setName("希腊披萨");
}else if(orderType.equals("cheese")){
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}
return pizza;
}
}
同时对OrderPizza进行修改:
package SimpleFactoryPattern.PizzaStore.Order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import SimpleFactoryPattern.PizzaStore.pizza.Pizza;
public class OrderPizza {
//定义一个简单工厂对象
SimpleFactory simpleFactory;
Pizza pizza = null;
public OrderPizza(SimpleFactory simpleFactory) {
setFactory(simpleFactory);
}
public void setFactory(SimpleFactory simpleFactory) {
//用户输入 要什么类型的披萨
String orderType = "";
//设置简单工厂对象
this.simpleFactory = simpleFactory;
do {
orderType = getType();
pizza = this.simpleFactory.creatPizza(orderType);
//输出pizza
if(pizza != null) { //订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println(" 订购披萨失败 ");
break;
}
} while (true);
}
private String getType() {
try {
//顾客告诉前台,要什么口味的披萨,前台在系统中选择
BufferedReader bfReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String string;
string = bfReader.readLine();
return string;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
}
客户端代码改变:
package SimpleFactoryPattern.PizzaStore.Order;
//相当于一个客户端,发出订购任务
public class PizzaStore {
public static void main(String[] args) {
//使用简单工厂模式
//将使用功能和创建产品分开执行
new OrderPizza(new SimpleFactory());
System.out.println("退出程序");
}
}
/*
结果:
input pizza type:
greek
使用简单工厂模式
希腊披萨材料准备好了!
希腊披萨baking
希腊披萨cutting
希腊披萨boxing
input pizza type:
cheese
使用简单工厂模式
奶酪披萨材料准备好了!
奶酪披萨baking
奶酪披萨cutting
奶酪披萨boxing
input pizza type:
xx
使用简单工厂模式
订购披萨失败
退出程序
*/
简单工厂模式的实现到此就结束了,但是简单工厂模式又被称为静态工厂方法。原因就是它把SimpleFactory类中的createPizza()方法变成了静态,同时代码进行了一些改动。
package SimpleFactoryPattern.PizzaStore.Order;
//简单工厂类
import SimpleFactoryPattern.PizzaStore.pizza.CheesePizza;
import SimpleFactoryPattern.PizzaStore.pizza.GreekPizza;
import SimpleFactoryPattern.PizzaStore.pizza.Pizza;
public class SimpleFactory {
//根据orderType 返回对应的Pizza对象
public Pizza creatPizza(String orderType){
Pizza pizza = null;
System.out.println("使用简单工厂模式");
if(orderType.equals("greek")) {
//创建不同的披萨对象
pizza = new GreekPizza();
pizza.setName("希腊披萨");
}else if(orderType.equals("cheese")){
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}
return pizza;
}
public static Pizza creatPizza2(String orderType){
Pizza pizza = null;
System.out.println("使用简单工厂模式2");
if(orderType.equals("greek")) {
//创建不同的披萨对象
pizza = new GreekPizza();
pizza.setName("希腊披萨");
}else if(orderType.equals("cheese")){
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}
return pizza;
}
}
OderPizza中方法的调用进行了改变:
package SimpleFactoryPattern.PizzaStore.Order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import SimpleFactoryPattern.PizzaStore.pizza.Pizza;
public class OrderPizza2 {
Pizza pizza = null;
String orderType = "";
public OrderPizza2() {
do {
orderType = getType();
pizza = SimpleFactory.creatPizza2(orderType);
//输出pizza
if(pizza != null) { //订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println(" 订购披萨失败 ");
break;
}
} while (true);
}
private String getType() {
try {
//顾客告诉前台,要什么口味的披萨,前台在系统中选择
BufferedReader bfReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String string;
string = bfReader.readLine();
return string;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
}
客户端:
package SimpleFactoryPattern.PizzaStore.Order;
//相当于一个客户端,发出订购任务
public class PizzaStore {
public static void main(String[] args) {
//使用简单工厂模式
/*
* new OrderPizza(new SimpleFactory());
* System.out.println("退出程序");
*/
new OrderPizza2();
}
}
3.4 模式分析
在简单工厂模式中,新的产品加入系统时,产品角色无须修改就可接纳新的产品,但对于工厂角色来说,增加新产品要修改源程序;工厂角色必须知道每一种产品,如何创建产品,以及何时向客户端提供产品。所以,简单工厂模式对于产品角色开﹣闭原则是成立的,而对于工厂角色开﹣闭原则是不成立的,简单工厂角色只在有限的程度上支持开﹣闭原则。
简单工厂模式的要点是当用户需要什么时,只需要传人一个正确的参数,就可以获取所需要的对象,而无须知道其创建细节。
3.4.1 简单工厂模式的优点
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;
- 简单工厂模式通过这法实现了对责任的分割,它提供了专门的工厂类用于创建对象。客户端无须知道所创建的具产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工一模式就可以减少用户的记忆量。新的产品加人系统时,产品角色无须修改就可被接纳。
3.4.2 简单工厂模式的缺点
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式如果使用了静态工厂方法,就会造成工厂角色无法形成基于继承的等级结构。
3.4.3 模式应用环境
- 工厂类负责创建的对象比较少。由于创建的对象较少,不会造就工厂方法中的业务逻辑太过复杂。
- 客户端只知道传人工厂类的参数,对于如何创建对象不关。客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。