类型:
工厂设计模式分为简单工厂、工厂方法、抽象工厂。
作用:
封装对象的创建。
一、简单工厂
以披萨店售卖披萨为例。
一家披萨店肯定不止一种披萨,所以建一个披萨超类,所有披萨都继承它。
abstract class Pizza {
String name;
public void prepare() {
System.out.println("通用: 准备");
}
public void bake() {
System.out.println("通用:烘烤");
}
public void cut() {
System.out.println("通用:切片");
}
public void box() {
System.out.println("通用:打包");
}
}
所有的披萨继承Pizza超类,超类中设置了一些制作披萨通用的方法。
我们需要:辣条披萨、老干妈披萨和奶酪披萨。
class LatiaoPizza extends Pizza {
public LatiaoPizza() {
name = "辣条披萨";
System.out.println("name: " + name);
}
/**
* 子类可以根据实际需求覆盖父类方法
*/
@Override
public void prepare() {
System.out.println(name + ":准备一些辣条");
}
@Override
public void cut() {
System.out.println(name + ":切成辣条形状");
}
}
class LaoganmaPizza extends Pizza {
public LaoganmaPizza(){
name = "老干妈披萨";
System.out.println("name: " + name);
}
@Override
public void prepare() {
System.out.println(name + ":准备一些老干妈");
}
}
class CheesePizza extends Pizza {
public CheesePizza() {
name = "奶酪披萨";
System.out.println("name: " + name);
}
}
披萨超类里面设置一些披萨通用的方法,子类披萨可以根据的自己的需求决定是否覆盖。
披萨店里有很多披萨,我们可以用一个工厂
类来管理披萨种类,工厂
是一种称呼。
根据披萨类型创建相应的披萨。
class SimplePizzaFactory {
public Pizza createPizza(String type){
Pizza pizza = null;
if (type.equals("cheese")){
pizza = new CheesePizza();
}else if (type.equals("辣条")){
pizza = new LatiaoPizza();
}else if (type.equals("老干妈")){
pizza = new LaoganmaPizza();
}
return pizza;
}
}
最后再创建一个我们的披萨店制作披萨的流程类。PizzaStore
class PizzaStore {
private SimplePizzaFactory mSimplePizzaFactory = null;
public PizzaStore(SimplePizzaFactory factory) {
this.mSimplePizzaFactory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
//使用简单工厂创建披萨
pizza = mSimplePizzaFactory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
到这里我们的披萨店就可以开业卖披萨了,可以尝试点上一份披萨了。
public static void main(String[] args) {
PizzaStore pizzaStore = new PizzaStore(new SimplePizzaFactory());
//奶酪披萨
Pizza pizza = pizzaStore.orderPizza("cheese");
//老干妈披萨
pizza = pizzaStore.orderPizza("老干妈");
//辣条披萨
pizza = pizzaStore.orderPizza("辣条");
}
运行结果如下:
name: 奶酪披萨
通用: 准备
通用:烘烤
通用:切片
通用:打包
name: 老干妈披萨
老干妈披萨:准备一些老干妈
通用:烘烤
通用:切片
通用:打包
name: 辣条披萨
辣条披萨:准备一些辣条
通用:烘烤
辣条披萨:切成辣条形状
通用:打包
因为披萨的类型有很多种,有可能会增加或者减少,所以使用简单工厂方法将披萨类的实例进行单独封装,用户类PizzaStore
不用再new
对象,将对象的创建交给工厂来做。
简单工厂严格来说不算工厂模式,更像是一种良好的编程习惯 – 将变化的地方进行封装。(封装变化
)。
使用静态方式定义简单工厂称为静态工厂
。
静态工厂不需要使用创建对象的方法来实例化对象。
二、工厂方法模式
如果我们的披萨店经营的很好,需要到四川、浙江、上海等其他地方开分店。并且每个地方的披萨可能会有每个地方的口味。这样仅用一个SimplePizzaFactory无法满足我们的需求。我们可以根据SimplePizzaFactory写出各地的工厂方法。比如四川的SCPizzaStore
,浙江的ZJPizzaStore
。
如果需要实现不同地区创建不同口味的披萨,此时我们就要把createPizza
拿回PizzaStore
了,并且把它设置为抽象,每个子类都必须实现它,创建不同口味的披萨。
abstract class PizzaStore {
/**
* 订购披萨
*/
public Pizza orderPizza(String type){
Pizza pizza;
//将CreatePizza方法从工厂对象中移回PizzaStore
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/**
* 工厂方法:
* 用来处理对象的创建,并将创建对象的行为封装在子类中。
* 这样超类的代码就和子类对象创建代码解耦了
*/
abstract Pizza createPizza(String type);
}
再来创建我们的四川SCPizzaStore
和浙江ZJPizzaStore
口味的PizzaStore
//四川风格
class SCPizzaStore extends PizzaStore {
/**
* 实现createPizza()方法,子类决定实例化哪一种具体Pizza,实现与父类解耦
*/
@Override
Pizza createPizza(String type) {
if (type.equals("麻辣")) {
return new SCStyle2Pizza();
} else if (type.equals("香辣")){
return new SCStyle1Pizza();
}
return null;
}
}
//浙江风格
class ZJPizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
if (type.equals("水煮")) {
return new ZJStyle1Pizza();
} else if (type.equals("清蒸")) {
return new ZJStyle2Pizza();
}
return null;
}
}
SCPizzaStore 和ZJPizzaStore 都继承自超类PizzaStore ,实现其createPizza()方法。
这样做的好处是:将对象的创建延迟到子类中,父类不在关心子类如何创建对象,实现了解耦。
我们的披萨也可能需要根据不同的地区来做出调整,此时我们超类Pizza调整为:
abstract class Pizza {
String name; //名字
String dough; //面团类型
String sauce; //调料
ArrayList toppings = new ArrayList();
public void prepare() {
System.out.println("准备:" + name);
System.out.print("上面添加调料:");
for (int i = 0; i < toppings.size(); i++) {
System.out.println(" " + toppings.get(i));
}
}
//烘焙
public void bake() {
System.out.println("通用:烘烤方式");
}
//切片
public void cut() {
System.out.println("通用:切片方式");
}
//打包
public void box() {
System.out.println("通用:打包方式");
}
public String getName() {
return name;
}
}
创建四川口味的披萨和浙江口味的披萨。都继承自Pizza超类。
//香辣披萨
class SCStyle1Pizza extends Pizza {
public SCStyle1Pizza(){
name = "四川:香辣披萨";
dough = "厚饼";
sauce = "配料:香精,红辣椒,辣椒油";
toppings.add("椒盐");
}
/**
* 覆盖父类切法
*/
@Override
public void cut() {
System.out.println("四川香辣披萨专属切法");
}
}
//麻辣披萨
class SCStyle2Pizza extends Pizza {
public SCStyle2Pizza(){
name = "四川:麻辣披萨";
dough = "厚饼";
sauce = "配料:麻,辣椒油";
toppings.add("上番茄酱");
}
@Override
public void cut() {
System.out.println("四川麻辣披萨专属切法");
}
}
//水煮披萨
class ZJStyle1Pizza extends Pizza {
public ZJStyle1Pizza(){
name = "浙江:水煮披萨";
dough = "薄饼";
sauce = "配料:蛤蜊";
toppings.add("鱼油");
}
//水煮披萨不用切,所以覆盖父类cut方法
@Override
public void cut() {
}
}
//清蒸披萨
class ZJStyle2Pizza extends Pizza {
public ZJStyle2Pizza(){
name = "浙江:清蒸披萨";
dough = "薄饼";
sauce = "配料:虾仁";
toppings.add("小葱");
}
}
到这里我们就可以点上一份香辣披萨和水煮披萨了
public static void main(String[] args) {
//订购一个四川口味的香辣披萨和一个浙江口味的水煮披萨
PizzaStore pizzaStore = new SCPizzaStore();
pizzaStore.orderPizza("香辣");
pizzaStore = new ZJPizzaStore();
pizzaStore.orderPizza("水煮");
}
运行结果:
准备:四川:香辣披萨
上面添加调料: 椒盐
通用:烘烤方式
四川香辣披萨专属切法
通用:打包方式
准备:浙江:水煮披萨
上面添加调料: 鱼油
通用:烘烤方式
通用:打包方式
总结:
工厂方法是用来处理对象的创建,并将这样的行为封装到子类中。这样超类的代码就和子类的创建解耦了
工厂方法模式定义:
定义了一个创建对象的接口,但由子类决定要实例化哪一个。工厂方法让类把实例化推迟到子类。
注:定义参照Head First。在Head First中超类也被称作接口。
说明:在此例中PizzaStore
为创建对象的接口,createPizza()
为工厂方法,定义为抽象类型,每个子类都必须实现createPizza()
方法,这样就达到了将类的实例推迟到子类中实现的效果。
问:PizzaStore
为什么也定义成abstract
抽象类型的呢?
符合设计原则 – 依赖倒置原则:依赖抽象,不依赖具体类。
问:简单工厂和工厂方法模式的区别?
简单工厂把类的创建都封装在一个地方创建,而工厂方法则是创建一个框架,由子类决定如何实现,更有弹性。
三、抽象工厂
加入我们的披萨店已经开了很多分店了,为了早日上市捞钱我们需要做到从原料到制作一条龙服务。因此我们现在需要解决原料的问题了。
1、创建原料工厂,负责生产披萨所需要的原料。
interface PizzaIngredientFactory {
//创建面团
Dough createDough();
//创建调料
Sauce createSauce();
//创建蔬菜
Veggies[] createVeggies();
//创建火腿
Pepperoni createPepperoni();
}
2、创建各地区的原料工厂
创建四川原料工厂
//四川原料工厂,返回四川口味披萨所需要的材料
class SCPizzaIngredientFactory implements PizzaIngredientFactory{
@Override
public Dough createDough() {
//四川面
return new SCDough();
}
@Override
public Sauce createSauce() {
//四川调料
return new SCSauce();
}
@Override
public Veggies[] createVeggies() {
//蔬菜
Veggies veggies[] = {new TomatoVeggies(), new CukeVeggies()};
return veggies;
}
@Override
public Pepperoni createPepperoni() {
//鸡肉火腿
return new ChickenPepperoni();
}
}
创建浙江原料工厂
//浙江原料工厂,返回浙江口味披萨所需要的材料
class ZJPizzaIngredientFactory implements PizzaIngredientFactory{
@Override
public Dough createDough() {
//浙江和四川都是用一种面
return new SCDough();
}
@Override
public Sauce createSauce() {
//浙江调料
return new ZJSauce();
}
@Override
public Veggies[] createVeggies() {
//蔬菜
Veggies veggies[] = {new TomatoVeggies(), new CukeVeggies()};
return veggies;
}
@Override
public Pepperoni createPepperoni() {
//鱼肉火腿
return new FishPepperoni();
}
}
3、重做Pizza
abstract class Pizza {
//每个披萨都会持有一组准备时会用到的原料
String name;
Dough mDough;
Sauce mSauce;
Veggies mVeggies[];
Pepperoni mPepperoni;
/**
* 把prepare()声明成抽象
* 因为不同地区准备的原料可能不同,所以由子类从原料工厂中自己索取
*/
abstract void prepare();
void bake(){
System.out.println("通用: 烘烤30分钟");
}
void cut(){
System.out.println("通用:切成三角形");
}
void box(){
System.out.println("通用:使用正方盒子打包");
}
public String getName() {
return name;
}
}
继续重做香辣披萨
class XLPizza extends Pizza {
PizzaIngredientFactory mIngredientFactory;
/**
* 构造方法初始化一个抽象工厂向pizza提供准备所需要的原料
* pizza只需要向工厂索取原料就行,不关心具体是什么地区的工厂。
* 实现pizza和区域原料之间的解耦
*/
public XLPizza(PizzaIngredientFactory pizzaIngredientFactory){
this.mIngredientFactory = pizzaIngredientFactory;
name = "香辣披萨";
}
@Override
void prepare() {
System.out.println("准备:" + name);
//初始化原料,从原料工厂中获取原料
mDough = mIngredientFactory.createDough();
mSauce = mIngredientFactory.createSauce();
mVeggies = mIngredientFactory.createVeggies();
mPepperoni = mIngredientFactory.createPepperoni();
System.out.println("准备成功: 从原料工厂获得原料");
}
}
清蒸披萨
class QZPizza extends Pizza {
PizzaIngredientFactory mIngredientFactory;
/**
* 使用抽象工厂向pizza提供准备所需要的原料
* pizza只需要向工厂索取原料就行,不关心具体是什么地区的工厂。
* 实现pizza和区域原料之间的解耦
*/
public QZPizza(PizzaIngredientFactory ingredientFactory){
this.mIngredientFactory = ingredientFactory;
name = "清蒸披萨";
}
/**
* 从工厂中获取原料
*/
@Override
void prepare() {
System.out.println("准备:" + name);
mDough = mIngredientFactory.createDough();
mSauce = mIngredientFactory.createSauce();
mVeggies = mIngredientFactory.createVeggies();
mPepperoni = mIngredientFactory.createPepperoni();
System.out.println("准备成功: 从原料工厂获得原料");
}
}
披萨代码利用相关工厂生产原料,所生产工的原料依赖所使用的工厂,Pizza不关心这些原料。Pizza和区域原料之间被解耦。
4、回到披萨店
四川披萨店
class SCPizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza = null;
//四川披萨店原料由四川原料工厂提供
PizzaIngredientFactory ingredientFactory = new SCPizzaIngredientFactory();
System.out.println("获得四川原料工厂");
if (type.equals("香辣")){
//将工厂传递给披萨
pizza = new XLPizza(ingredientFactory);
}else if (type.equals("麻辣")){
pizza = new MLPizza(ingredientFactory);
}
return pizza;
}
}
浙江的披萨店:
class ZJPizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza = null;
//浙江披萨店原料由浙江原料工厂提供
PizzaIngredientFactory ingredientFactory = new ZJPizzaIngredientFactory();
System.out.println("获得浙江原料工厂");
if (type.equals("清蒸")){
//将工厂传递给披萨
pizza = new QZPizza(ingredientFactory);
}else if (type.equals("水煮")){
pizza = new SZPizza(ingredientFactory);
}
return pizza;
}
到这里我们已经完工了,可以点一份披萨了
public static void main(String[] args) {
//订购一个香辣披萨一个清蒸披萨
PizzaStore pizzaStore = new SCPizzaStore();
pizzaStore.orderPizza("香辣");
pizzaStore = new ZJPizzaStore();
pizzaStore.orderPizza("清蒸");
}
运行结果:
获得四川原料工厂
准备:香辣披萨
准备成功: 从原料工厂获得原料
通用: 烘烤30分钟
通用:切成三角形
通用:使用正方盒子打包
获得浙江原料工厂
准备:清蒸披萨
准备成功: 从原料工厂获得原料
通用: 烘烤30分钟
通用:切成三角形
通用:使用正方盒子打包
经过上面的改造,我们做了什么?
引入了新类型的工厂,也就是抽象工厂来创建披萨的原料家族。
通过抽象工厂所提供的接口,可以创建产品的家族,利用这个接口书写代码,代码可以从实际工厂解耦,以便在不同的上下文中使用各式各样的工厂,制造出各种不同的产品。例如:不同区域,不同外观,不同操作系统。
抽象工厂模式定义:
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦
本例中原料工厂PizzaIngredientFactory
就是一个被抽象出来的工厂,Pizza只需要向抽象的原料工厂PizzaIngredientFactory获取原料就行,而不需要关心是四川的原料工厂还是浙江的原料工厂。
同时可以看到抽象工厂里的每个创建对象的方法都被声明为抽象的,并且子类覆盖并创建对象,这其实是以工厂方法的方式实现的。
问:抽象工厂和工厂方法的区别?
1、工厂方法使用的是继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中
2、抽象工厂适合创建产品家族和想让制造的相关产品集合起来。
工厂方法可以将客户代码从需要实例化的具体类中解耦。
依赖倒置原则:依赖抽象,不依赖具体类
参考:<<Head First 设计模式>>