简单工厂模式
在我们平时的编码过程中,我们会经常使用到new,而当我们使用new的时候,就会想到"具体",因为我们的确是在实例化一个具体类,这样子,可能会导致代码更脆弱。比如说,我们可能会写出以下代码:
Duck duck;
if(picnic){
duck = new MallardDuck();
}else if (hunting){
duck = new DecoyDuck();
}else if(inBathTub){
duck = new RubberDuck();
}
当出现这种代码时,一旦需求有变化,就必须重新打开这段代码进行检查和修改。通常这样修改过的diam将造成部分系统更难维护和更新,而且更容易犯错。
披萨的例子
当我们订购一个披萨的时候,我们可能会写出如下代码:
public class PizzaStore{
Pizza orderPizza(String type){
Pizza pizza;
//根据披萨的类型,我们实例化正确的具体类
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
但是一旦我们pizza的种类变化,我们就不得不修改if else语句。因此我们需要把这一部分进行封装。
建立简单工厂
public class SimplePizzaFactory{
public Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
return pizza;
}
}
除此之外,我们还可以将工厂定义为静态方法,这是我们常见的一个技巧,因为不需要使用创建对象的方法来实例化对象,但是这也有确定,不能通过集成来改变创建方法的行为。
我们的PizzaStore变成了如下:
public class PizzaStore{
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
Pizza orderPizza(String type){
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
kotlin实现
class SimpleFactory {
fun createPizza(type : String) : Pizza{
lateinit var pizza : Pizza
if (type == "NY"){
pizza = NYPizza()
}else if (type == "Chi"){
pizza = ChiPizza()
}
return pizza
}
}
class PizzaStore constructor(factory : SimpleFactory){
private var factory : SimpleFactory = factory
fun orderPizza(type : String) : Pizza{
val pizza = factory.createPizza(type)
pizza.bake()
pizza.prepare()
return pizza
}
}
定义简单工厂
简单工厂其实不是一个设计模式,它比较像是一种编程习惯,但是我们会经常用到它。
工厂模式
假设我们又增加了新的需求,我们的pizza店需要生产更多的披萨,我们需要根据不同区域的口味,生成不同种类的披萨,这时我们的PizzaStore就会变成这样
这样就会有越来越多的工厂,分别产生不同口味的披萨。因此,我们可以通过创建多个“披萨店”分别制造不同口味的披萨,除此之外,我们加工披萨的流程也会产生差异,因此我们需要对我们的pizzaStore做出些改变,将加工pizza的流程和pizza的制作流程捆绑在一起的同时又具有一定弹性。
代码
我们的PizzaStore现在变成了这样:
PizzaStore.java
public abstract class PizzaStore {
// 原本是由一个类类负责所有具体类的实例化,现在变成由一堆子类来负责实例化
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);//工厂方法是抽象的,所以依赖子类来处理对象的创建。 工厂方法必须返回一个对象
}
NYPizzaStore.java
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")){
return new NYStyleCheesePizza();
}else if (type.equals("veggie")){
return new NYStyleVeggiePizza();
}else {
return null;
}
}
}
ChicagoPizzaStore.java
public class ChicagoPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")){
return new ChicagoStyleCheesePizza();
}else {
return null;
}
}
}
我们让PizzaStore的各个子类负责定义自己的createPizza()方法,所以我们会得到一些PizzaStore具体的子类。
那子类是如何做决定的呢?当orderPizza()调用createPizza()方法的时候,某个披萨店的子类(NYStylePizzaStore或者ChicagoStylePizzaStore)来负责创建披萨。
下面,我们看一下我们的Pizza类:
Pizza.java
import java.util.ArrayList;
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
void prepare(){
System.out.println("Preparing " + name );
System.out.println("Tossing dough...");
System.out.println("Ading sauce...");
System.out.println("Adding toppings: ");
for (int i = 0; i < toppings.size(); i++){
System.out.println(" " + toppings.get(i));
}
}
void bake(){
System.out.println("Bake for 25 minutes at 350");
}
void cut(){
System.out.println("Cutting the pizza into diagonal slices");
}
void box(){
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
}
现在我们需要一些具体子类,来定义纽约和芝加哥风味的芝士披萨。
芝加哥风味的:
ChicagoStyleCheesePizza.java
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza(){
name = "Chicago Style Deep Dish Cheese Pizzza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
}
void cut(){
System.out.println("Cutting the pizza into square slices");
}
}
纽约风味的
NYStyleCheesePizza.java
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza(){
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Cruse Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
NYStyleVeggiePizza.java
public class NYStyleVeggiePizza extends Pizza {
public NYStyleVeggiePizza(){
name = "NY Style Sauce and Veggie Pizza";
dough = "Thin Cruse Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Veggie");
}
}
测试类:
public class PizzaTestDrive {
public static void main(String[] args){
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a "+ pizza.getName() + "\n");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel Ordered a "+ pizza.getName() + "\n");
}
}
工厂模式类图
创建者类:
所有工厂模式都是用来封装对象的创建。工厂模式通过让子类决定该创建对象是什么,来达到将对象创建的过程封装的目的。
PizzaStore里面定义了一个抽象的工厂方法createPizza(),让子类实现此方法,来制造产品。这些子类称为具体创建者。
产品类:
上图这些就是具体的产品。
定义工厂模式
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类吧实例化推迟到子类。
工厂模式类图
所有的产品必须实现这个共同的接口Product,这样,使用这些产品的类就可以引用这个接口,而不是具体类。
Creator是一个类,它实现了所有操纵产品的方法,但是它不实现工厂方法。Creator的子类必须实现factoryMethod这个抽象方法,来实际制造出产品。ConcreteCreator类负责创建一个或多个具体的ConcreteProduct。
工厂模式和简单工厂比较
简单工厂的做法,可以将对象的创建封装起来,在一个地方都处理完了;但是工厂方法只是提供了一个框架,由子类决定要如何实现。
依赖倒置原则
不能让高层组件依赖底层组件,无论是高层组件还是底层组件,都应该依赖于抽象。
抽象工厂
我们的披萨制造过程中是由不同的原料组成的,纽约风味的披萨使用一组原料,而芝加哥使用另一组原料。每一种原料都包含一种棉短,一种酱料,一种芝士以及一种海鲜。因此,我们需要处理一下这些原料。
建造原料工厂
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
在这个接口中,每个原料都有一个对应的方法创建该原料。我们要做的是,为每一种区域都建造一个工厂,比如说,我们先创建纽约原料工厂。
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
@Override
public Veggies[] createVeggies() {
Viggies viggies[] = {new Garlic(), new Onion(),new MushRoom()};
return viggies;
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
return new FreshClams();
}
}
重做pizza
import java.util.ArrayList;
public abstract class Pizza {
String name;
Dough dough;
Sause sauce;
ArrayList toppings = new ArrayList();
abstract void prepare();
void bake(){
System.out.println("Bake for 25 minutes at 350");
}
void cut(){
System.out.println("Cutting the pizza into diagonal slices");
}
void box(){
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
}
重做CheesePizza
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory = ingredientFactory;
}
@Override
void prepare() {
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
}
}
再看我们的披萨店
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
// 纽约店会用到纽约披萨原料工厂,由该工厂负责生产所有纽约风味比萨所需的原料。
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (type.equals("cheese")){
pizza = new CheesePizza(ingredientFactory);
return pizza;
}else if (type.equals("veggie")){
// TODO
}
}
}
我们公共抽象工厂所提供的接口,可以创建产品家族,利用这个接口,我们的代码将从实际的工厂解耦。
定义抽象工厂模式
抽象工厂模式提供了一个接口,用于创建相关或依赖对象的家族,而不需要明确指明具体类。
从图中可以看出,抽象工厂的任务是定义一个负责创建一组产品的接口,这个接口内的每个方法都负责创建一个具体产品。同时,我们利用实现抽象工厂的子类来提供这些具体的做法,所以,在抽象工厂中利用工厂方法实现生产方法是相当自然的做法。
比较工厂方法和抽象工厂
工厂方法和抽象工厂都是负责创建对象,但是工厂方法使用的是继承,但是抽象工厂使用的是对象的组合。这就意味着,利用工厂方法创建对象,需要扩展一个类,并覆盖他的工厂方法。但是抽象工厂提供了一个用来创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法。