《设计模式》学习笔记2——简单工厂模式

简介: 定义 简单工厂模式并不属于GoF(Gang of Four四人组)23中设计模式,有些地方的解释说因为简单工厂模式太简单,所以23中设计模式就没有单独列出。 但是简单工厂模式在实际的应用中却很常用,因此在刘伟老师的《设计模式》一书中就还是列了出来。

定义

简单工厂模式并不属于GoF(Gang of Four四人组)23中设计模式,有些地方的解释说因为简单工厂模式太简单,所以23中设计模式就没有单独列出。
但是简单工厂模式在实际的应用中却很常用,因此在刘伟老师的《设计模式》一书中就还是列了出来。
简单工厂模式引用书中的定义如下:

简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式

理解

对于这个模式,我觉得说静态工厂模式比说简单工厂模式更能让人记住。静态工厂模式,通过静态两个字就可以知道需要有一个静态的工厂方法,然后只要再理解一下何谓工厂,基本上就记住了这个模式。
那么何谓工厂呢,众所周知,生产产品的地方就是工厂。
对于工厂而言,需要有能正常运行的生产线,这个生产线可能是全自动化的,也可能是人工操作的,除此之外,还需要有被生产的产品。
根据上边的思路,我们便可以转换为代码,首先写出我们的产品类:

package patterntest.simplefactorypattern;

/**
 * 产品类
 * 
 * @author tzx
 *
 */
public class MyProduct {
    /**
     * 产品名称
     */
    private String productName;
    /**
     * 产品规格
     */
    private String productSize;

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getProductSize() {
        return productSize;
    }

    public void setProductSize(String productSize) {
        this.productSize = productSize;
    }

    @Override
    public String toString() {
        return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]";
    }

}

有了这样一个产品类,便可以创建该类的具体产品实例,学习java的必然知道最基本的创建对象实例的方式就是使用new关键字,比如new MyProduct()。就像下边的代码:

package patterntest.simplefactorypattern;

/**
 * 消费者
 * 
 * @author tzx
 *
 */
public class Consumer {

    public static void main(String[] args) {
        System.out.println("我需要一个产品,于是我愉快的制造了一个");
        MyProduct product = new MyProduct();
        System.out.println("我制造的产品是这样的:" + product.getProductSize());
    }

}

这种方式其实相当于现实生活中某个人需要一个什么东西,然后自己制造一个出来。
这种情况现实生活中必然是存在的,但是也必然有一定的限制,不可能每个人都能制造每种东西,所以还有很多东西个人制造不出来,或者说个人制造的成本太高,因此便有了专门制造某类东西的工厂,也就是我们的工厂类:

package patterntest.simplefactorypattern;

/**
 * 简单工厂
 * 
 * @author tzx
 *
 */
public class MySimpleFactory {

    /**
     * 外部获取产品的工厂方法
     */
    public MyProduct getProduct() {

        return new MyProduct();
    }
}

那么这个时候,我们需要用某个东西的人,简称为消费者,就不需要自己再制造这类东西,也不需要如何制造这类东西,可以更加方便的使用:

package patterntest.simplefactorypattern;

/**
 * 消费者
 * 
 * @author tzx
 *
 */
public class Consumer {

    public static void main(String[] args) {
        System.out.println("我需要一个产品,于是我向工厂买了一个");
        MyProduct product = new MySimpleFactory().getProduct();
        System.out.println("我买的产品是这样的:" + product.getProductSize());
    }

}

这样一来,我们需要的东西只需要调用工厂的购买方法,或者说工厂提供给外边的获取产品的方法,然后就能获取到想要的产品了。
然而,有一个很不愉快的问题出现了,上边的代码中,产品确实不需要自己创建,不需要自己new了,但是工厂却是被我们new出来的。
这样问题就大了,相当于我仅仅需要某个产品,然后为了这个产品,我必须自己创建了一个工厂,而且每个需要这个产品的人都要创建一个该产品的工厂,这显然是不合理也不可能的。
我们需要的仅仅是这样而已:

MyProduct product = MySimpleFactory.getProduct();

那么工厂方法也就需要改成静态的,使我们只要用类名就可以调用获取产品的方法:

/**
 * 外部获取产品的工厂方法
 */
public static MyProduct getProduct() {

    return new MyProduct();
}

好了,现在对于某个产品,工厂就合理的提供了一个给外边获取产品的方法,而外边的消费者也能通过一声MySimpleFactory.getProduct()的呼唤愉快的获取到需要的产品了。
然而,如果后边这个工厂扩大了,开始运营的产品不再单一了,例如牙膏,一开始只是成年人用的牙膏,后边又增加了儿童牙膏,那么很显然这时候我们工厂提供的获取产品的无参方法就难以继续担当大任了。
因为只通过一声我要买牙膏的呼唤,工厂根本无法知道消费者需要的是哪种,所以我们的工厂方法需要有参数,并且根据不同的参数给予不同的产品:

public static MyProduct getProduct(int type) {
    return new MyProduct();
}

现在,基本解决了上边不知道消费者具体要什么的问题,但是,工厂内部却犯难了,因为根据原本产品制造说明说(产品类的定义),工厂能制造出来的产品只有一种啊。
所以,原本的产品也需要升级,也就是本身的默认无参构造器不能满足现有的需求了,我们可能需要增加有参数的构造方法,需要升级原本的产品制造说明书,并通过参数来确定具体的产品。

public MyProduct(int type) {
    if (0 == type) {
        // 提供成年人牙膏
    } else if (1 == type) {
        // 提供儿童牙膏
    }
}

那么以上的一些问题也暂时解决了,不管是需要儿童牙膏还是普通牙膏,工厂都能正确的提供,消费者也都能正确的获取。

public static MyProduct getProduct(int type) {
    return new MyProduct(type);
}

只是,如果后边工厂又扩大了,还要加入环保牙膏、特效美白牙膏呢?那么我们就需要不断的修改原本的产品构造方法,就如一本产品制造说明书要继续加厚。
很显然,这将导致那个有参数的构造方法越来越臃肿,如果每种产品的制造方法都异常复杂的话,我们的一本产品制造说明书也将可能达到需要人抬的地步。
所以,这样不是办法,我们需要把各自具体产品的制造说明书分开,也就是我们的产品类需要由一个变成多个:

/**
 * 普通牙膏
 * 
 * @author tzx
 *
 */
public class MyProduct1 {
    /**
     * 产品名称
     */
    private String productName;
    /**
     * 产品规格
     */
    private String productSize;

    public MyProduct1() {
        addIngredient();
    }

    /**
     * 添加必要成分
     */
    public void addIngredient() {

    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getProductSize() {
        return productSize;
    }

    public void setProductSize(String productSize) {
        this.productSize = productSize;
    }

    @Override
    public String toString() {
        return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]";
    }
}
/**
 * 儿童牙膏
 * 
 * @author tzx
 *
 */
public class MyProduct2 {
    /**
     * 产品名称
     */
    private String productName;
    /**
     * 产品规格
     */
    private String productSize;

    public MyProduct2() {
        addIngredient();
        delead();
    }

    /**
     * 去铅
     */
    public void delead() {

    }

    /**
     * 添加必要成分
     */
    public void addIngredient() {

    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getProductSize() {
        return productSize;
    }

    public void setProductSize(String productSize) {
        this.productSize = productSize;
    }

    @Override
    public String toString() {
        return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]";
    }
}

明眼人应该一眼就看出了上述代码的问题,这两个类几乎一模一样,唯独不同的就是儿童牙膏增加了一个去铅的方法。
很显然,这样的代码是严重重复,不被看好的代码,十足的浪费资源。所以我们需要把这些共同的东西给提取出来,形成一个父类,而这个父类不是具体的产品类,就需要声明为抽象类,然后我们的产品类就应该是这样了:

/**
 * 产品类父类
 * 
 * @author tzx
 *
 */
public abstract class MyProduct {
    /**
     * 产品名称
     */
    protected String productName;
    /**
     * 产品规格
     */
    protected String productSize;

    /**
     * 添加必要成分
     */
    public void addIngredient() {

    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getProductSize() {
        return productSize;
    }

    public void setProductSize(String productSize) {
        this.productSize = productSize;
    }

    @Override
    public String toString() {
        return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]";
    }

}
/**
 * 普通牙膏
 * 
 * @author tzx
 *
 */
public class MyProduct1 extends MyProduct {
    public MyProduct1() {
        addIngredient();
    }
}
/**
 * 儿童牙膏
 * 
 * @author tzx
 *
 */
public class MyProduct2 extends MyProduct {
    public MyProduct2() {
        addIngredient();
        delead();
    }

    /**
     * 去铅
     */
    public void delead() {

    }
}

那么工厂类中具体产品的选择,就可以改成下边这样:

/**
 * 外部获取产品的工厂方法
 */
public static MyProduct getProduct(int type) {
    MyProduct product = null;
    if (0 == type) {
        product = new MyProduct1();
    } else if (1 == type) {
        product = new MyProduct2();
    }
    return product;
}

要点

好了,一个比较标准的简单工厂模式就出来了,由上边的分析可以知道简单工厂模式的几个要点:

  1. 需要有一个生产产品的产品类(工厂)和静态的输出产品的方法,这个方法包含一些必要判断逻辑;
  2. 需要有一个抽象的产品父类,定义产品的公共属性和方法;
  3. 需要有具体的产品类;
  4. 只要提供正确的参数,就能通过静态工厂方法获取到具体的产品。

总结

通过上边的一系列分析和实例,可以知道简单工厂模式的一些优点,他可以使消费者不必关心具体产品的创建,只需要一个参数就能得到需要的产品,实现了一定程度的松耦合。
同时,不同的产品不同的子类,也使得后续更容易拓展。
但是由于所有产品都通过工厂创建,不同产品获取的逻辑判断都在工厂方法中,因此具体的产品和工厂的耦合度就必然增大,同时在产品类的基础上也额外的增加了工厂类。
所以,如果产品很多的话,这种简单工厂模式便会变得臃肿而不易维护。

demo源码可在github下载:https://github.com/tuzongxun/mypattern

目录
相关文章
|
17天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
19天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
12天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
31 1
|
1月前
|
设计模式 Java Kotlin
Kotlin学习笔记 - 改良设计模式 - 迭代器模式
Kotlin学习笔记 - 改良设计模式 - 迭代器模式
27 2
|
1月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
21 3
|
2月前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。
|
1月前
|
设计模式 JavaScript Scala
Kotlin学习笔记 - 改良设计模式 - 责任链模式
Kotlin学习笔记 - 改良设计模式 - 责任链模式
42 0
|
1月前
|
设计模式 Java Kotlin
Kotlin 学习笔记- 改良设计模式 - 装饰者模式
Kotlin 学习笔记- 改良设计模式 - 装饰者模式
26 0