当我们在实际开发的过程中,经常会用到new这个关键字,需要用到什么类,就new什么类,非常的方便,但是我们在享受这种便利的同时,这种便利也给我们带来了一些麻烦:
使用new关键字之后,会让类之间的耦合度变高,比如说:Fruit fruit=new Apple(),这样就写死了,如果想把fruit修改成别的什么水果,就必须去改代码,这不符合我们的开闭原则(不了解的可以看这里)
于是我们引入了简单工厂来解耦
简单工厂
先假设以下情景,假如你有一家饮品店,用户要自己点饮料
我们先看看如果不用工厂模式我们该怎么实现这个需求,下面写一段伪代码:
public class BeverageStore {
public Beverage order(String type){
Beverage beverage=null;
if ("奶茶".equals(type))
beverage = new Milkytea();
else if ("咖啡".equals(type))
beverage = new Coffee();
beverage.prepare();
beverage.brew();
beverage.stir();
beverage.pack();
return beverage;
}
}
我们可以看到,依赖关系之间写死在了代码里面,如果想要增加新的产品,必须去修改源代码,这不符合开闭原则
我们来看看简单工厂是如何做的
先看看类图:
看看代码:
Beverage
public class Beverage {
String name;
String water;
String basis;
ArrayList<String> ingredient=new ArrayList<String>();
public String getName(){
return this.name;
}
public void prepare(){
System.out.println("准备:"+name);
}
public void brew(){
System.out.println("加水:"+name);
}
public void stir(){
System.out.println("搅拌:"+name);
}
public void pack(){
System.out.println("打包:"+name);
}
@Override
public String toString() {
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
display.append(water + "\n");
display.append(basis + "\n");
for (int i = 0; i < ingredient.size(); i++) {
display.append(ingredient.get(i) + "\n");
}
return display.toString();
}
}
Coffee
public class Coffee extends Beverage{
public Coffee(){
name="咖啡";
water="纯净水";
basis="咖啡粉";
ingredient.add("糖");
}
}
Milkytea
public class Milkytea extends Beverage{
public Milkytea(){
name="奶茶";
water="纯净水";
basis="奶粉";
ingredient.add("香料");
}
}
SimpleBeverageFactory
public class SimpleBeverageFactory {
public Beverage createBeverage(String type){
Beverage beverage=null;
if ("奶茶".equals(type))
return new Milkytea();
else if ("咖啡".equals(type))
return new Coffee();
else
return beverage;
}
}
BeverageStore
public class BeverageStore {
SimpleBeverageFactory factory;
public BeverageStore(SimpleBeverageFactory factory){
this.factory=factory;
}
public Beverage order(String type){
//把得到饮料的过程委托给了工厂去做,只关心工厂给自己的返回,并不关心返回的是什么
Beverage beverage=factory.createBeverage(type);
beverage.prepare();
beverage.brew();
beverage.stir();
beverage.pack();
return beverage;
}
}
写个测试方法
public static void main(String[] args) {
SimpleBeverageFactory factory=new SimpleBeverageFactory();
BeverageStore store=new BeverageStore(factory);
Beverage coffee=store.order("咖啡");
System.out.println(coffee);
}
结果:
准备:咖啡
加水:咖啡
搅拌:咖啡
打包:咖啡
---- 咖啡 ----
纯净水
咖啡粉
糖
我们可以看到,其实简单工厂所做的只是把原先产品和商店之间的依赖转移到了产品和工厂,也不符合开闭原则,因为你要增加新的产品,你虽然不需要去修改商店了,但是还是要去修改工厂。当然,在规模很小的时候,简单工厂也够用了。
工厂方法
随着我们规模的扩大,我们开了不少连锁店,我们发现,在有些地方我们销量并不理想,于是针对不同地方人的口味进行产品的调整势在必行,但是简单工厂已经不能满足我们的生产需求了(产品种类越来越多,集中在一个工厂里面每次增加产品都要修改源代码),于是我们引入了工厂方法。
先来看看类图:
我们针对不同地区的人的口味对产品做了微调,使其更符合不同地区人的口味,不同地区的人,只要找到对应的店就行了,他们不用关心其它地方的产品,本地的店也能更加专注于对本地人的服务。
我们来看看代码
Beverage
public class Beverage {
String name;
String water;
String basis;
ArrayList<String> ingredient=new ArrayList<String>();
public String getName(){
return this.name;
}
public void prepare(){
System.out.println("准备:"+name);
}
public void brew(){
System.out.println("加水:"+name);
}
public void stir(){
System.out.println("搅拌:"+name);
}
public void pack(){
System.out.println("打包:"+name);
}
@Override
public String toString() {
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
display.append(water + "\n");
display.append(basis + "\n");
for (int i = 0; i < ingredient.size(); i++) {
display.append(ingredient.get(i) + "\n");
}
return display.toString();
}
}
GuangxiMilkytea
public GuangxiMilkytea(){
name="广西奶茶";
water="广西纯净水";
basis="广西奶粉";
ingredient.add("广西香料");
}
GuangxiCoffee
public class GuangxiCoffee extends Beverage{
public GuangxiCoffee(){
name="广西咖啡";
water="广西纯净水";
basis="广西咖啡粉";
ingredient.add("广西糖");
}
}
HunanCoffee
public class HunanCoffee extends Beverage{
public HunanCoffee(){
name="湖南咖啡";
water="湖南纯净水";
basis="湖南咖啡粉";
ingredient.add("湖南糖");
}
}
HunanMilkytea
public class HunanMilkytea extends Beverage{
public HunanMilkytea(){
name="湖南奶茶";
water="湖南纯净水";
basis="湖南奶粉";
ingredient.add("湖南香料");
}
}
BeverageStore
public abstract class BeverageStore {
//只定义了制作饮料的方法,并没有指定制作什么,交给子类自己去实现,使代码更具有弹性
abstract Beverage createBeverage(String type);
public Beverage order(String type){
Beverage beverage=createBeverage(type);
beverage.prepare();
beverage.brew();
beverage.stir();
beverage.pack();
return beverage;
}
}
HunanStore
public class HunanStore extends BeverageStore{
Beverage createBeverage(String type) {
Beverage beverage=null;
if ("咖啡".equals(type))
//因为是湖南店,所以用符合湖南口味的咖啡
return new HunanCoffee();
//湖南口味的奶茶
return new HunanMilkytea();
else
return beverage;
}
}
GuangxiStore
public class GuangxiStore extends BeverageStore{
Beverage createBeverage(String type) {
Beverage beverage=null;
if ("咖啡".equals(type))
//广西口味的咖啡
return new GuangxiCoffee();
else if("奶茶".equals(type))
//广西口味奶茶
return new GuangxiMilkytea();
else
return beverage;
}
}
我们来写个测试方法
public static void main(String[] args) {
//找到湖南的店
BeverageStore hnStore= new HunanStore();
//要喝什么
Beverage beverage=hnStore.order("奶茶");
System.out.println(beverage.toString());
}
运行结果:
准备:湖南奶茶
加水:湖南奶茶
搅拌:湖南奶茶
打包:湖南奶茶
---- 湖南奶茶 ----
湖南纯净水
湖南奶粉
湖南香料
我们看看工厂方法做了什么?
- 他其实是把一个混合的工厂拆分成了多个更加专一的多个工厂
- 父类工厂规范了制作流程,子类工厂去完成具体实现
我们来看看这样对比简单工厂有何好处:
1. 假如湖南店要增加一个产品,那么用简单工厂去做的话,要修改整个混合工厂,出错会导致所有连锁店瘫痪,而工厂方法则只需要修改湖南店的代码,风险控制在了湖南店,出错只会影响到湖南店
2. 假如我们要开发湖北地区的特殊产品,如果用简单工厂,风险在所有连锁店上,如果我们用工厂方法,则新建一个湖北商店就行,对现有的系统没有什么影响
相信大家看到这里对简单工厂和工厂方法已经有了一个比较深的理解了
因为抽象工厂相对来说比较复杂,所以我把抽象工厂单独抽出来讲
PS:源码地址