工厂模式
问题回顾:
ArrayList<String> list=new ArrayList<>();
代码绑定者具体实现类,会让代码脆弱且缺乏弹性。我们可以使用接口使它更有弹性:
List<String> list = new ArrayList<>();
Duck duck = new MallardDuck();
但有一些要实例化的具体类,究竟实例化为哪一个类,需要由运行时的条件决定。一旦有变化或扩展,就必须重新打开上面那段代码检查修改,这将令维护更新变得困难。
在这个例子中,new一个对象没有错,真正有问题的是“改变”没被隔离出来,影响了new的使用。
设计原则:类应当对扩展开放,对修改关闭。
针对接口编程可以隔离掉系统以后将发生的一大堆改变。我们需要一种方法找出会变化的部分,把它们从不变的部分隔离出来。
再看一个例子:
Pizza orderPizza(String type){
Pizza pizza;
/*************创建对象部分************/
if(type.equals("a"))pizza=new APizza();
else if(type.equals("b"))pizza=new BPizza();
else{/*...*/}
/***********************************/
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
如果披萨的种类不断增加,那么这段代码就必须一改再改。进而我们知道,如果在代码中new某个对象(实例化某个对象),将使orderPizza()出问题,无法让orderPizza()对修改关闭。所以我们得做一些封装。
将创建Pizza对象转移到orderPizza()之外,给专职创建披萨的新对象,即工厂。
**工厂(Factory)**处理创建对象的细节
¶简单工厂:
public class SimplePizzaFactory{
public Pizza createPizza(String type){
Pizza pizza=null;
if(type.equals("a"))pizza=new APizza();
else if(type.equals("b"))pizza=new BPizza();
else{/*...*/}
return pizza;
}
}
利用静态方法定义一个简单工厂:静态工厂:
public class SimplePizzaFactory{
public static Pizza createPizza(String type){
Pizza pizza=null;
if(type.equals("a"))pizza=new APizza();
else if(type.equals("b"))pizza=new BPizza();
else{/*...*/}
return pizza;
}
}
¶优点
帮助封装。客户端可以更好的面向接口编程。
解耦。客户端不需要知道实现类。
¶缺点
需要理解参数的意义,增加复杂度。
静态工厂不方便扩展子类。
简单工厂其实不是一种设计模式,更像是一种编程习惯
升级原有代码,用工厂去创建披萨:
public class PizzaStore{
SimplePizzaFactory factory;
//构造注入
public PizzaStore(SimplePizzaFactory factory){
thi.factory=factory;
}
Pizza orderPizza(String type){
Pizza pizza=factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
¶工厂模式:
让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。工厂模式的组成元素:创建者类和产品类
¶创建者:
/*创建者类*/
public abstract class PizzaStore{
Pizza orderPizza(String type){
Pizza pizza = createPizza(type); //创建对象的业务
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type); //让子类决定该创建的对象是什么
}
//创建者类:纽约披萨店
public class NYPizzaStore extends PizzaStore{
//子类决定该创建的对象是什么
Pizza createPizza(String item){
if(item.equals("cheese")){
return new NYStyleCheesePizza();
}else if(item.equals("clam")){
return new NYStyleClamPizza();
}else if(...){
...
}return null;
}
}
//创建者类:洛杉矶披萨店
public class LAPizzaStore extends PizzaStore{
//子类决定该创建的对象是什么
Pizza createPizza(String item){
if(item.equals("cheese")){
return new LAStyleCheesePizza();
}else if(item.equals("clam")){
return new LAStyleClamPizza();
}else if(...){
...
}return null;
}
}
....
¶产品:
public abstract class Pizza{
String name;
String dough;
String sauce;
void prepare(){
System.out.println("preparing...");
}
void bake(){
System.out.println("baking...");
}
void cut(){
System.out.println("cutting...");
}
void box(){
System.out.println("boxing...");
}
public String getName() {
return name;
}
}
...
public class LAStyleCheesePizza extends Pizza{
LAStyleCheesePizza(){
name="NEW YORK STYLE CHEESE PIZZA";
dough="dough";
sauce="sauce";
toppings.add("LA Cheese");
}
@Override
void cut() {
System.out.println("cutting in LA Style...");
}
}
...
public class NYStyleCheesePizza extends Pizza {
NYStyleCheesePizza(){
name="NEW YORK STYLE CHEESE PIZZA";
dough="dough";
sauce="sauce";
toppings.add("Cheese");
}
}
Test:
public class Test {
public static void main(String[] args) {
PizzaStore nyStore=new NYPizzaStore();
PizzaStore laStore=new LAPizzaStore();
System.out.println("吃一个"+nyStore.orderPizza("cheese").getName());
System.out.println();
System.out.println("吃一个"+laStore.orderPizza("cheese").getName());
}
}
工厂方法:
- 是抽象的,具体的实现封装在子类中。
- 将返回一个产品类
- 将业务和实际创建具体产品的代码分隔开来。
¶工厂好处:
- 将创建对象的代码集中在一个对象或方法中,可以避免代码中的重复,并更方便以后的维护。也意味着客户在实例化对象时,只会依赖于接口而不是具体类。帮助我们针对接口编程。
¶依赖倒置原则(Dependency Inversion Principle):
设计原则:要依赖抽象,不要依赖具体类
不能让高层组件依赖底层组件,不管是高层底层组件,都应当依赖于抽象
PizzaStore是高层组件,披萨实现是底层组件。
避免违反依赖导致原则的指导方针:
- 变量不可以持有具体类的引用
如果使用new,就会持有具体类的引用,你可以改用工厂方法来避免这么做- 不要让类派生自具体类
如果派生自具体类,就会依赖具体类。请派生自一个抽象(接口或抽象类)- 不要覆盖基类中已实现的方法
如果覆盖基类已实现的方法,那么这个基类就不适合作为被继承的抽象。基类中已实现的方法要由子类共享。
¶抽象工厂:
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
通过抽象工厂所提供的接口,可以创建产品的家族,利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同的上下文实现各式各样的工厂,制造不同的商品。
我们引入了抽象工厂来创建 “披萨原料”家族 。
披萨店:
public abstract class PizzaStore {
protected abstract Pizza createPizza(String item);
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
/*************纽约披萨店***************/
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
// 原料工厂
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (item.equals("cheese")) {
// 注入抽象原料工厂
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
} else if (item.equals("veggie")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("New York Style Veggie Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
} else if (item.equals("pepperoni")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");
}
return pizza;
}
}
/*************芝加哥披萨店***************/
public class ChicagoPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
switch (item){
case "cheese":
pizza = new CheesePizza(ingredientFactory);
pizza.setName("Chicago Style Cheese Pizza");
break;
case "veggie":
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("Chicago Style Veggie Pizza");
break;
case "clam":
pizza = new ClamPizza(ingredientFactory);
pizza.setName("Chicago Style Clam Pizza");
break;
case "pepperoni":
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("Chicago Style Pepperoni Pizza");
break;
}
return pizza;
}
}
披萨:
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings=new ArrayList();
void prepare(){
System.out.println("preparing "+name);
}
void bake(){
System.out.println("baking...");
}
void cut(){
System.out.println("cutting...");
}
void box(){
System.out.println("boxing...");
}
public String getName() {
return name;
}
}
/*******************************************************/
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
//披萨构造函数注入抽象原料工厂,运行时动态地注入对应地区原料工厂,造出对应地区口味披萨
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
/*******************************************************/
public class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
clam = ingredientFactory.createClam();
}
}
......
原料工厂:
public interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
Veggies[] createVeggies();
Pepperoni createPepperoni();
Clams createClam();
}
/*********************纽约原料工厂***********************/
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
Veggies veggies[] = {new Garlic(), new Onion(), new Mushroom(), new RedPepper()};
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClam() {
return new FreshClams();
}
}
/*********************芝加哥原料工厂***********************/
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThickCrustDough();
}
@Override
public Sauce createSauce() {
return new PlumTomatoSauce();
}
@Override
public Cheese createCheese() {
return new MozzarellaCheese();
}
@Override
public Veggies[] createVeggies() {
Veggies[] veggies = {
new BlackOlives(),
new Spinach(),
new Eggplant()
};
return veggies;
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
return new FrozenClams();
}
}
......
一图胜千言:
抽象工厂的任务是定义一个负责创建一组产品的接口。这个接口内的每一个方法都负责创建一个具体产品,同时我们利用实现抽象工厂的子类来提供这些方法的具体做法。
在抽象工厂中利用工厂方法实现生产方法是相当自然的。
¶比较抽象工厂模式和工厂模式
工厂方法 | 抽象工厂 |
---|---|
负责创建对象,采用继承方式:利用工厂方法创建对象时需要扩展一个类,并覆盖它的工厂方法,即通过子类来创建对象。 | 负责创建对象,采用组合方式:提供一个用来创建产品家族的抽象类型,这个类型的子类定义了产生产品的方法。要想使用抽象工厂,必须先实例化它,然后将实例传入针对抽象类型所写的代码中。 |
抽象创建者(creator)中的方法 经常会用到子类所创建的具体类型 | 实现抽象类型的具体工厂 经常使用工厂方法来创建他们的产品 |
将客户代码从需要实例化的具体类中解耦。如果目前不知道将来需要实例化哪些类时,也可以用工厂模式。 | 创建产品家族,或想让制造的相关产品集合起来时,可以使用抽象工厂模式 |
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,邮件至 708801794@qq.com
文章标题:工厂模式
文章字数:2.4k
本文作者:梅罢葛
发布时间:2020-03-28, 14:21:29
最后更新:2020-03-28, 22:23:16
原始链接:https://qiurungeng.github.io/2020/03/28/%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/