草庐IT

设计模式之工厂模式

pluto_charon 2023-03-28 原文

介绍:

工厂模式专门负责及那个大量有共同接口的类实例化,工厂模式可以动态决定奖哪一个类实例化,不必事先知道每次要实例化哪一个类。工厂模式有一下几种形态:

  • 简单工厂模式:又称静态工厂方法模式,是不同的工厂方法模式的一个特殊实现。
  • 工厂方法模式:又称多态性工厂模式
  • 抽象工厂模式:又称工具箱模式

简单工厂模式:

比如说有一个农场公司,专门向市场销售以下水果:葡萄(Grape)、草莓(Strawberry)、苹果(Apple)。

水果接口规定出所有的水果必须实现的接口:种植plant()、生长grow()、收获harvest().

其UML类图如下:

那么水果接口的代码如下:

package com.charon.factory.simpleFactory;

/**
 * @className: Fruit
 * @description: 水果接口
 * @author: charon
 * @create: 2022-03-06 20:30
 */
public interface Fruit {

    /**
     * 种植
     */
    void plant();

    /**
     * 生长
     */
    void grow();

    /**
     * 收获
     */
    void harvest();
}

草莓类是水果类的一种,因此他实现了水果接口中所有声明的方法。

package com.charon.factory.simpleFactory;

/**
 * @className: Strawberry
 * @description:
 * @author: charon
 * @create: 2022-03-06 20:43
 */
public class Strawberry implements Fruit{

    @Override
    public void plant() {
        System.out.println("草莓种植了。。。。");
    }

    @Override
    public void grow() {
        System.out.println("草莓生长中。。。。");
    }

    @Override
    public void harvest() {
        System.out.println("草莓收获了。。。。");
    }
}

苹果类也是水果类的一种,因此他实现了水果接口中所有声明的方法:

package com.charon.factory.simpleFactory;

/**
 * @className: Apple
 * @description:
 * @author: charon
 * @create: 2022-03-06 20:36
 */
public class Apple implements Fruit{

    @Override
    public void plant() {
        System.out.println("苹果树种植了。。。。");
    }

    @Override
    public void grow() {
        System.out.println("苹果树生长中。。。。");
    }

    @Override
    public void harvest() {
        System.out.println("苹果树收获了。。。。");
    }
}

葡萄类也是水果类的一种,因此他实现了水果接口中所有声明的方法:

package com.charon.factory.simpleFactory;

/**
 * @className: Grape
 * @description:
 * @author: charon
 * @create: 2022-03-06 20:41
 */
public class Grape implements Fruit{

    @Override
    public void plant() {
        System.out.println("葡萄种植了。。。。");
    }

    @Override
    public void grow() {
        System.out.println("葡萄生长中。。。。");
    }

    @Override
    public void harvest() {
        System.out.println("葡萄收获了。。。。");
    }
}

FruitGardener类是园丁类,会根据客户端的要求,创建出不同的水果对象,所有的创建对象的任务都由这个类完成。

package com.charon.factory.simpleFactory;

/**
 * @className: FruitGardener
 * @description: 园丁类
 * @author: charon
 * @create: 2022-03-06 20:46
 */
public class FruitGardener {

    /**
     * 静态工厂方法
     *
     * @param fruitType 水果类型
     * @return
     */
    public static Fruit factory(String fruitType) {
        if ("apple".equalsIgnoreCase(fruitType)) {
            return new Apple();
        } else if ("Grape".equalsIgnoreCase(fruitType)) {
            return new Grape();
        } else if ("Strawberry".equalsIgnoreCase(fruitType)) {
            return new Strawberry();
        } 
        return null;
    }
}

测试:

package com.charon.factory.simpleFactory;

/**
 * @className: Test
 * @description:
 * @author: charon
 * @create: 2022-03-06 20:50
 */
public class Test {
    public static void main(String[] args) {
        Fruit apple = FruitGardener.factory("apple");
        apple.plant();
        apple.grow();
        apple.harvest();
        Fruit grape = FruitGardener.factory("grape");
        grape.plant();
        grape.grow();
        grape.harvest();
        Fruit strawberry = FruitGardener.factory("strawberry");
        strawberry.plant();
        strawberry.grow();
        strawberry.harvest();
    }
}
打印结果:
    苹果树种植了。。。。
    苹果树生长中。。。。
    苹果树收获了。。。。
    
    葡萄种植了。。。。
    葡萄生长中。。。。
    葡萄收获了。。。。
    
    草莓种植了。。。。
    草莓生长中。。。。
    草莓收获了。。。。

简单工厂模式就是由一个工厂类根据传入的参数决定创建出哪一种产品的类的实例。

这种模式的优点:好理解,简单易操作。

这种模式的缺点:违反了设计模式的ocp原则,即对扩展开放,对修改关闭。

如果我们需要新添加一种水果,那么需要新添加一个水果类,还需要在FruitGardener中添加这种水果的实例化。同时,由于使用的静态方法作为的工厂方法,而静态方法无法由子类继承,因此,工厂角色无法形成基于继承的等级结构。

从上面可以知道,简单工厂模式涉及到工厂角色、抽象产品角色以及具体产品角色:

  • 工厂角色:担任这个角色的是工厂方法模式的核心,含有与应用紧密相关的商业逻辑,工厂类在客户端的直接调用下创建产品对象,往往由一个具体的java类实现
  • 抽象产品角色:担任这个角色的类是由工厂方法模式所创建的对象的父类,或他们共同拥有的接口,抽象产品角色可以用一个java接口或者java抽象类实现
  • 具体产品角色:工厂方法模式所创建的任何对象都是这个角色的实例,具体产品角色由一个个具体的java类实现

工厂方法模式:

工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,也克服了他的缺点。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做,这个核心类则成为了一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不是接触哪一个产品类应当被实例化这种细节。

还是上面的例子。由于农场规模扩大,一个园丁已经处理不过来了,所以现在每一种水果都需要专门的园丁来管理了。那么上面的FruitGardener这个全能角色就成了抽象的园丁角色,这个角色规定出具体园丁角色需要实现的具体职能,而真正负责水果管理的则是具体的园丁角色。

本系统的UML类图如下:

FruitGardener接口类:

package com.charon.factory.factoryMethod;

import com.charon.factory.simpleFactory.Fruit;

/**
 * @className: FruitGardener
 * @description: 园丁类
 * @author: charon
 * @create: 2022-03-06 20:46
 */
public interface FruitGardener {

    /**
     * 静态工厂方法
     *
     * @param fruitType 水果类型
     * @return
     */
    Fruit factory();

}

AppleGardener类是具体工厂类,它实现了FruitGardener 接口,提供了工厂方法的实现:

package com.charon.factory.factoryMethod;

import com.charon.factory.simpleFactory.Apple;
import com.charon.factory.simpleFactory.Fruit;

/**
 * @className: AppleGardener
 * @description:
 * @author: charon
 * @create: 2022-03-06 23:04
 */
public class AppleGardener implements FruitGardener{
    @Override
    public Fruit factory() {
        return new Apple();
    }
}

GrapeGardener类是具体工厂类,它实现了FruitGardener 接口,提供了工厂方法的实现:

package com.charon.factory.factoryMethod;

import com.charon.factory.simpleFactory.Fruit;
import com.charon.factory.simpleFactory.Grape;

/**
 * @className: GrapeGardener
 * @description:
 * @author: charon
 * @create: 2022-03-06 23:04
 */
public class GrapeGardener implements FruitGardener{
    @Override
    public Fruit factory() {
        return new Grape();
    }
}

StrawberryGardener类是具体工厂类,它实现了FruitGardener 接口,提供了工厂方法的实现:

package com.charon.factory.factoryMethod;

import com.charon.factory.simpleFactory.Fruit;
import com.charon.factory.simpleFactory.Strawberry;

/**
 * @className: StrawberryGardener
 * @description:
 * @author: charon
 * @create: 2022-03-06 23:04
 */
public class StrawberryGardener implements FruitGardener{
    @Override
    public Fruit factory() {
        return new Strawberry();
    }
}

Test类:

package com.charon.factory.factoryMethod;

import com.charon.factory.simpleFactory.Fruit;

/**
 * @className: Test
 * @description:
 * @author: charon
 * @create: 2022-03-06 23:11
 */
public class Test {

    public static void main(String[] args) {
        FruitGardener appleGardener = new AppleGardener();
        Fruit apple = appleGardener.factory();
        apple.plant();
        apple.plant();
        apple.harvest();

        FruitGardener grapeGardener = new GrapeGardener();
        Fruit grape = grapeGardener.factory();
        grape.plant();
        grape.plant();
        grape.harvest();

        FruitGardener strawberryGardener = new StrawberryGardener();
        Fruit strawberry = strawberryGardener.factory();
        strawberry.plant();
        strawberry.plant();
        strawberry.harvest();
    }
}
打印结果:
    苹果树种植了。。。。
    苹果树生长中。。。。
    苹果树收获了。。。。
    
    葡萄种植了。。。。
    葡萄生长中。。。。
    葡萄收获了。。。。
    
    草莓种植了。。。。
    草莓生长中。。。。
    草莓收获了。。。。

从上面可以知道,工厂方法模式涉及到抽象工厂角色、具体工厂角色、抽象产品角色以及具体产品角色:

  • 抽象工厂角色:担任这个角色的是工厂方法模式的核心,它是与应用程序无关的。任何在模式中创建对象的工厂类必须实现这个接口。这个角色也常常使用抽象Java类实现。
  • 具体工厂角色:担任这个角色的是实现了抽象工厂接口的具体Java类。具体工厂角色含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
  • 抽象产品角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。这个角色也常常使用抽象Java类实现。
  • 具体产品角色:这个角色实现了抽象产品角色所声明的接口。工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。

抽象工厂模式:

抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象。这就是抽象工厂模式的用意。

为了方便引进抽象工厂模式,特地引进了一个新的概念:产品族,是指与不同产品等级结构中功能相关联的产品组成的家族。

还是上面的例子。随着市场的扩大,农场公司再次面临新的发展,引入了蔬菜大棚技术,在大棚内种植热带和亚热带的水果和蔬菜。

因此在这个系统里,产品分成两个等级结构:水果(热带水果,亚热带水果)和蔬菜(热带蔬菜,亚热带蔬菜)。园丁呢,也分成两类:管理热带水果蔬菜的园丁(tropicGardener)和管理亚热带水果蔬菜的园丁(subtropicalGardener)。

本系统的UML类图如下:

Fruit 水果接口类:

package com.charon.factory.absFactory;

/**
 * @className: Fruit
 * @description: 水果类
 * @author: charon
 * @create: 2022-03-08 22:12
 */
public interface Fruit {
}

TropicFruit 热带水果实现类:

package com.charon.factory.absFactory;

/**
 * @className: TropicFruit
 * @description: 热带水果
 * @author: charon
 * @create: 2022-03-08 22:13
 */
public class TropicFruit implements Fruit{

    private String name;

    public TropicFruit(String name) {
        this.name = name;
        System.out.println("热带水果: "+ name);
    }
}

SubtropicFruit 亚热带水果实现类:

package com.charon.factory.absFactory;

/**
 * @className: TropicFruit
 * @description: 亚热带水果
 * @author: charon
 * @create: 2022-03-08 22:13
 */
public class SubtropicFruit implements Fruit{

    private String name;

    public SubtropicFruit(String name) {
        this.name = name;
        System.out.println("亚热带水果: "+ name);
    }
}

Veggie 蔬菜类接口:

package com.charon.factory.absFactory;

/**
 * @className: Veggie
 * @description: 蔬菜类
 * @author: charon
 * @create: 2022-03-08 22:12
 */
public interface Veggie {
}

TropicVeggie 热带蔬菜:

package com.charon.factory.absFactory;

/**
 * @className: TropicVeggie
 * @description: 热带蔬菜
 * @author: charon
 * @create: 2022-03-08 22:13
 */
public class TropicVeggie implements Veggie{

    private String name;

    public TropicVeggie(String name) {
        this.name = name;
        System.out.println("热带蔬菜: "+ name);
    }
}

SubtropicVeggie 亚热带蔬菜:

package com.charon.factory.absFactory;

/**
 * @className: TropicVeggie
 * @description: 热带蔬菜
 * @author: charon
 * @create: 2022-03-08 22:13
 */
public class SubtropicVeggie implements Veggie{

    private String name;

    public SubtropicVeggie(String name) {
        this.name = name;
        System.out.println("亚热带蔬菜: "+ name);
    }
}

Gardener 园丁接口类:

package com.charon.factory.absFactory;

/**
 * @className: Gardener
 * @description: 园丁的顶级接口
 * @author: charon
 * @create: 2022-03-08 22:09
 */
public interface Gardener {

    /**
     * 创建亚热带水果
     * @return
     */
    Fruit createFruit();

    /**
     * 创建亚热带蔬菜
     * @return
     */
    Veggie createVeggie();
}

TropicGardener 热带园丁类:

package com.charon.factory.absFactory;

/**
 * @className: TropicGardener
 * @description: 管理热带水果蔬菜的园丁类
 * @author: charon
 * @create: 2022-03-08 22:10
 */
public class TropicGardener implements Gardener{

    /**
     * 创建热带水果
     * @return
     */
    @Override
    public Fruit createFruit(){
        return new TropicFruit("苹果");
    }

    /**
     * 创建热带蔬菜
     * @return
     */
    @Override
    public Veggie createVeggie(){
        return new TropicVeggie("白菜");
    }
}

SubtropicGardener 亚热带园丁类:

package com.charon.factory.absFactory;

import java.util.concurrent.Future;

/**
 * @className: SubtropicGardener
 * @description: 管理亚热带水果蔬菜的园丁类
 * @author: charon
 * @create: 2022-03-08 22:11
 */
public class SubtropicGardener implements Gardener{

    /**
     * 创建亚热带水果
     * @return
     */
    @Override
    public Fruit createFruit(){
       return new SubtropicFruit("苹果");
    }

    /**
     * 创建亚热带蔬菜
     * @return
     */
    @Override
    public Veggie createVeggie(){
        return new SubtropicVeggie("白菜");
    }
}

test 测试类:

package com.charon.factory.absFactory;


/**
 * @className: Test
 * @description:
 * @author: charon
 * @create: 2022-03-08 22:23
 */
public class Test {

    public static void main(String[] args) {
        Gardener tropicGardener = new TropicGardener();
        tropicGardener.createFruit();
        tropicGardener.createVeggie();

        Gardener subtropicGardener = new SubtropicGardener();
        subtropicGardener.createFruit();
        subtropicGardener.createVeggie();
    }
}

抽象工厂模式涉及到一下的角色:

  • 抽象工厂角色:担任这个角色的是工厂方法模式的核心,它是与应用系统的商业逻辑无关的。通常是使用java接口或者抽象java类实现,而所有的具体工厂类必须实现这个java接口或继承这个抽象java类
  • 具体工厂类角色:这个角色直接在客户端的调用下创建产品的实例,这个角色含有选择的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。通常使用具体java类实现的这个角色
  • 抽象产品角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或他们共同拥有的接口,通常使用java接口或者抽象java类实现这一角色
  • 具体产品角色:抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例,这事客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑,通常使用具体的java类实现这个角色

有关设计模式之工厂模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - Railstutorial : db:populate vs. 工厂女孩 - 2

    在railstutorial中,作者为什么选择使用这个(代码list10.25):http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-usersnamespace:dbdodesc"Filldatabasewithsampledata"task:populate=>:environmentdoRake::Task['db:reset'].invokeUser.create!(:name=>"ExampleUser",:email=>"example@railstutorial.org",:passwo

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  5. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  6. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  7. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  8. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  9. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  10. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

随机推荐