前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式 | 工厂模式

设计模式 | 工厂模式

作者头像
程序员小榆
发布2024-04-03 18:24:08
770
发布2024-04-03 18:24:08
举报
文章被收录于专栏:程序员小榆

我们将探讨 23 种设计模式中的工厂模式,属于创建型模式。主要目的是封装对象的创建过程。

比如说某某电子厂,通常会接到不同商家的订单,根据商家提供对应的图纸,生产并组装出成品,交付给客户。

商家无需知道这产品的制造和组装过程,只需根据商家提供的图纸,还原成品即可。

面向直接开发

例如您看到疯狂星期四非常火爆,也想开一家自己的疯狂汉堡餐厅,并且为这家疯狂汉堡餐厅创建了一个用于配送汉堡的应用程序。

现在,我们配送的每种口味的汉堡都由一个类来表示,根据我们已经研制出的汉堡种类,并支持汉堡的配送和生产代码。

代码语言:javascript
复制
public class Restaurant
{
    public ??? OrderBurger(string request)
    {
        //牛肉汉堡
        if ("beef".Equals(request))
        {
            BeefBurger burger = new BeefBurger();
            burger.prepare();
            return burger;
        }
        else if ("veggie".Equals(request))//素汉堡
        {
            VeggieBurger burger = new VeggieBurger();
            burger.prepare();
            return burger;
        }
        return null;
    }
}

目前来说,我们根据客户需求来制作对应的汉堡,创建相对应的'汉堡'对象,并返回给客户。这里偏向于面向直接开发方式,的确可以最为直观和快速。

设计抽象类

我们很快就会发现问题,这样的写法有一些限制,目前只能创建 BeefBurger 和 VeggieBurge 对象代表不同类型的产品,且返回单一类型的单一对象。

因此,如果这些产品没有共同的基类和接口,我们将无法很好地实现并且返回指定的对象,且存在大量的冗余的属性。

代码语言:javascript
复制
//汉堡抽象类
public abstract class Burger
{
    //产品id
    public int productId { get; set; }
    //添加配菜
    public string addOns { get; set; }
    public abstract Burger prepare();
}
//牛肉堡
public class BeefBurger : Burger
{
    //是否安格斯牛
    public bool angus { get; set; }
    public override Burger prepare()
    {
        return new BeefBurger();
    }
}
//素堡
public class VeggieBurger : Burger
{
    //是否安格斯牛
    public bool angus { get; set; }
    public override Burger prepare()
    {
        return new VeggieBurger();
    }
}

通常,我们会建立一个基类 Burger,将两个汉堡的类抽象为一个类,还可以将两个汉堡的共同属性提取到抽象类 Burger,因为 BeefBurger 和 VeggieBurger 继承至 Burger 之后,相应的属性和方法已经实现。

代码语言:javascript
复制
public class RestaurantCover
{
    public Burger OrderBurger(string request)
    {
        Burger burger = null;
        //牛肉汉堡
        if ("beef".Equals(request))
        {
            burger = new BeefBurger();
        }
        else if ("veggie".Equals(request))//素汉堡
        {
            burger = new VeggieBurger();
        }
        return burger;
    }
}

我们的代码经过改进,实现了简易的扩展。但我们的餐厅现在还不够疯狂,随时面临顾客的不同口味需求,我们不得不扩展我们的菜单,添加更多种类的汉堡。

工厂设计模式

一旦发生扩展,我们不得不更改上面的代码。这样的改动不仅会影响之前稳定运转的程序,还违背了开放-封闭原则和单一职责原则。

每次扩展都对这些相同的位置进行更改且冗余的代码,这一定很糟糕不想这么干。

因此,我们不如把那些容易发生变化的进行封装起来,当我们的餐厅随着时间的推移而发展和变化时,我们可以直接对菜单进行新增,修改,删除。

我们把创建汉堡的流程进行封装起来,将其分离到一个 Factory 的类中。

为什么是工厂?因为这是一个唯一负责制作汉堡的类,交给汉堡生产部门进行生产汉堡。

代码语言:javascript
复制
public class SimpleBurgerFactory
{
    public Burger CreateBurger(string request)
    {
        Burger burger = null;
        //牛肉汉堡
        if ("beef".Equals(request))
        {
            burger = new BeefBurger();
        }
        else if ("veggie".Equals(request))
        {
            burger = new VeggieBurger();
        }
        return burger;
    }
}

我们需要汉堡时统一调用这个方法,他们就会给到我们需要的汉堡。

代码语言:javascript
复制
public class Restaurant
{
    public Burger orderBurger(string request)
    {
        SimpleBurgerFactory factory = new SimpleBurgerFactory();
        Burger burger = factory.CreateBurger(request);
        burger.prepare();
        return burger;
    }
}

现在汉堡工厂有了,我们要做的就是联系汉堡工厂,将我们的汉堡需求订单给到它,然后我们需要的食物就已经获得了。

我们不再需要担心具体的汉堡制作细节问题,也不需要注意请求汉堡的类型,我们只负责接收汉堡订单需求。无论是何种类型的汉堡,最终它都是实现 Burger 接口,这就是我们疯狂汉堡餐厅所关心的。

我们将这种设计模式称为简单工厂模式

我们目前拥有了客户端餐厅作为接收顾客的汉堡需求订单,拥有了工厂,它是唯一的地方,所提供的汉堡种类是已知的,分别为牛肉汉堡和素汉堡。

目前来说,我们疯狂汉堡店的设计模式并不是成熟的官方模式,它更像是一种常用的习惯设计思路。

工厂方法设计模式

当我们理解了这个思路,我们就可以用上更为强大的设计模式:工厂方法设计模式。此模式也是一种创建型的设计模式。

它通过将产品的创建代码部分与使用该产品的代码部分进行分离,以此减少给定代码的耦合度。

我们将结合疯狂汉堡店的示例来说明,为什么说现在实现的设计模式不是一种成熟的设计模式。

当我们将汉堡的制作逻辑封装在简单工厂类中,我们隔离了该逻辑并创建了一个 SimpleBurgerFactory 类,目的是为了创建汉堡对象。这里我们遵循了单一职责原则,只负责创建汉堡对象。

但是仔细看这个类的代码,我们发现还是会有改动,比如我们增加了鸡肉汉堡,我们就需要在这里进行添加代码,更多的汉堡类型时将会出现更多的 if 逻辑代码。

为了解决这个缺陷,我们将引入工厂方法设计模式。要改造当前简单工厂,我们需要删除 SimpleBurgerFactory 简单工厂类,并将 CreateBurger() 创建汉堡的逻辑和 Restaurant 餐厅的 orderBurger订单 都放回到 Restaurant 餐厅中,从头开始升级。

接下来我们要做的第一件事,就是抽象餐厅类,为什么这么做?

之前存在于简单工厂类中的 CreateBurger() 方法,现在将成为 Restaurant 类中的抽象方法,该方法是工厂方法,将交给 Restuarant 子类去实现,并基于在汉堡上,我们提供的这些子类分别是 BeefBurgerRestaurant 和 VeggieBurgerRestaurant。

代码语言:javascript
复制
//牛肉堡餐厅
public class BeefBurgerRestaurant : Restaurant
{
    public override Burger CreateBurger()
    {
        return new BeefBurger();
    }
}
//素堡餐厅
public class VeggieBurgerRestaurant : Restaurant
{
    public override Burger CreateBurger()
    {
        return new VeggieBurger();
    }
}

上面代码示例中,我们可以看到这实际上是给定代码中的精确定位工厂的使用方式, 因为工厂方法设计模式严重依赖于继承,它将对象的创建委托给实现的子类工厂方法。

我们的汉堡产品定义接口后,我们就可以让创建者子类决定要实现哪个类,这才是我们在这个实现过程中需要做的。

好,那现在剩下的就是用工厂方法替换业务逻辑中的创建代码。

代码语言:javascript
复制
public abstract class Restaurant
{
    public IBurger orderBurger()
    {
        IBurger burger = CreateBurger();
        burger.Prepare();
        return burger;
    }
    public abstract IBurger CreateBurger();
}
代码语言:javascript
复制
//牛肉堡餐厅
public class BeefBurgerRestaurant : Restaurant
{
    public override IBurger CreateBurger()
    {
        return new BeefBurger();
    }
}
//素堡餐厅
public class VeggieBurgerRestaurant : Restaurant
{
    public override IBurger CreateBurger()
    {
        return new VeggieBurger();
    }
}
代码语言:javascript
复制
//Burger 接口
public interface IBurger
{
    void Prepare();
}
//牛肉堡实现类
public class BeefBurger : IBurger
{
    public void Prepare()
    {
        //todo
    }
}
//素堡实现类
public class VeggieBurger : IBurger
{
    public void Prepare()
    {
        //todo
    }
}

此时的 orderBurger() 方法不再依赖于请求对象,这是因为我们作为用户不需要它,而是由餐厅的员工直接实例化并调用。

代码语言:javascript
复制
public static void main(string[] args)
{
    Restaurant beefRes = new BeefBurgerRestaurant();
    IBurger beefBurger = beefRes.orderBurger();

    Restaurant veggieRes = new VeggieBurgerRestaurant();
    IBurger veggieBurger = veggieRes.orderBurger();
}

餐厅员工需要的具体餐厅类实现,并且正确选择汉堡将返回给餐厅员工。最终使用 UML 类图表示如下。

到了这里,我们已经实现了工厂方法设计模式。如果你已经学会了如何使用它,那我们该在什么情况下使用该设计模式呢?

如果说,我们实现并不知道代码将使用的对象的确切类型和依赖项,那么很好的暗示我们一开始就应该引入工厂方法设计模式。

因为该工厂方法可以轻松扩建产品,构建代码独立于应用程序的其余部分。

更重要的是,我们将产品创建代码集中在程序中的一处时,我们可以遵循并应用开放-封闭原则和单一职责原则。

如何进一步升级?

好,在此基础之上我们引起思考:假如我们的疯狂汉堡餐厅蒸蒸日上,即将开分店招揽更多的生意,且汉堡产品口味相同,但是采用中式制作汉堡。

如果交付到应用程序的话,那我们该如何设计呢?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-03-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员小榆 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 面向直接开发
  • 设计抽象类
  • 工厂设计模式
  • 工厂方法设计模式
  • 如何进一步升级?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档