欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

设计模式(1)——工厂模式

程序员文章站 2024-01-21 14:44:16
...

什么是简单工厂模式?

在现实生活中工厂是负责生产产品的,同样在设计模式中,简单工厂模式我们也可以理解为负责生产对象的一个类, 我们平常编程中,当使用”new”关键字创建一个对象时,此时该类就依赖与这个对象,也就是他们之间的耦合度高,当需求变化时,我们就不得不去修改此类的源码,此时我们可以运用面向对象(OO)的很重要的原则去解决这一的问题,该原则就是——封装改变,既然要封装改变,自然也就要找到改变的代码,然后把改变的代码用类来封装,这样的一种思路也就是我们简单工厂模式的实现方式了。下面通过一个的例子来引出简单工厂模式。

工厂需要建造一批货,他们分别是四边形跟三边形,那么我们就要创建Quadrilateral类和Triangle类。首先四边形的定义是什么,一个有四条边的形状。三边形是一个有三条边的形状。

//四边形
class Quadrilateral
{
    //边数
    int Sides
    {
        get { return 4; }
    }
}
//三边形
class Triangle
{
    //边数
    int Sides
    {
        get { return 3; }
    }
}

有细心的人看到了,边数这个属性四边形和三边形都有。那假如我们要多造几个五边形六边形,那么就要好多类,而且代码很多重复,那么我们就要用到面向对象三大特性之一——继承。我们创建一个形状类。

class Quadrilateral:Sharp
{
    public override int Sides
    {
        get { return 4; }
    }
}

class Triangle:Sharp
{
    public override int Sides
    {
        get { return 3; }
    }
}

class Sharp
{
    public virtual int Sides { get; }
}

于是我们就用以下方式对这两个例子进行初始化。

Sharp quadrilateral = new Quadrilateral();
Sharp triangle = new Triangle();

但是实际上,我们都要在创建实例的时候做点初始化的工作,比如赋值,查询数据库等。于是就会用构造函数来实现,就会写成下面这样。

Sharp quadrilateral = new Quadrilateral(属性);
Sharp triangle = new Triangle(属性);

但是如果初始化工作不是赋值这种简单的操作,而是很长的代码,如果也写到构造函数中就很难看了。而且不仅如此,假如这次需要一个四边形,是个梯形,下次需要个长方形,下次又需要个正方形,这些需要的参数种类可能不同,数量也不同,就要写多个构造函数,或者需要个边长为5mm的正方形或者边长为3mm的正方形等等,于是每次改需求,就要每次去找到Sharp quadrilateral = new Quadrilateral(属性)这块代码对属性进行修改。但是其实这些都是工厂的需求,跟形状这个类本身关系不大,于是简单工厂模式应运而生。

class SharpFactory
{
    public static Sharp GetSharp(类型)
    {
        Sharp sharp = null;
        switch (类型)
        {
            case 四边形: sharp = new Quadrilateral();break;
            case 三角形: sharp = new Triangle();break;
            case 梯形: sharp = new Quadrilateral(new double[4] { 1, 2, 3, 3});break;
            case 长方形: sharp = new Quadrilateral(new double[4] { 2, 2, 3, 3 });break;
            case 3mm的正方形: sharp = new Quadrilateral(new double[4] { 3, 3, 3, 3 });break;
            case 5mm的正方形: sharp = new Quadrilateral(new double[4] { 5, 5, 5, 5 });break;
        }
        return sharp;
    }
}
//修改Quadrilateral类
class Quadrilateral:Sharp
{
    public override int Sides
    {
        get { return 4; }
    }
    public double[] Border { get; set; }
    public Quadrilateral(double[] border)
    {
        this.Border = border;
    }
    public Quadrilateral() { }
}
//调用
Sharp sharp = SharpFactory.GetSharp(类型);

其实上面还有几个不太合理的地方,比如这个Sharp类,他本身就是个形状,他不是一个实实在在存在的东西,他应该不能实例化,应该作为一个抽象类存在,当然了单纯改个abstract没什么意义,于是我决定对本次的例子进行扩展,增加一个周长的属性与一个求周长的方法。

class Quadrilateral:Sharp
{
    public override int Sides
    {
        get { return 4; }
    }
    public Quadrilateral(double[] border)
    {
        if (border.Count() != Sides)
            return;
        this.Border = border;
    }
    public Quadrilateral() { }

    public override void GetPerimeter()
    {
        Perimeter= Border.Sum();
    }
}

class Triangle:Sharp
{
    public override int Sides
    {
        get { return 3; }
    }
    public Triangle(double[] border)
    {
        if (border.Count() != Sides)
            return;
        this.Border = border;
    }
    public Triangle() { }
    public override void GetPerimeter()
    {
        Perimeter = Border.Sum();
    }
}

abstract class Sharp
{
    public virtual int Sides { get; }
    public double[] Border { get; set; }
    public double Perimeter { get; set; }
    public abstract void GetPerimeter();
}

发现好多重复代码,那继续改。

class Quadrilateral:Sharp
{
    public override int Sides
    {
        get { return 4; }
    }
}

class Triangle:Sharp
{
    public override int Sides
    {
        get { return 3; }
    }
}

abstract class Sharp
{
    private double[] border;
    public virtual int Sides { get; }
    public double[] Border
    {
        get { return border; }
        set
        {
            if (border.Count() == Sides)
                border = value;
        }
    }
    public double Perimeter { get; set; }
    public void GetPerimeter()
    {
        Perimeter = Border.Sum();
    }
}

class SharpFactory
{
    public static Sharp GetSharp(类型)
    {
        Sharp sharp = null;
        switch (类型)
        {
            case 四边形: sharp = new Quadrilateral(); break;
            case 三角形: sharp = new Triangle(); break;
            case 梯形: sharp = new Quadrilateral(); sharp.Border = new double[4] { 1, 2, 3, 3 }; break;
            case 长方形: sharp = new Quadrilateral(); sharp.Border = new double[4] { 2, 2, 3, 3 }; break;
            case 3mm的正方形: sharp = new Quadrilateral(); sharp.Border = new double[4] { 3, 3, 3, 3 }; break;
            case 5mm的正方形: sharp = new Quadrilateral(); sharp.Border = new double[4] { 5, 5, 5, 5 }; break;
        }
        return sharp;
    }
}

优点与缺点

  看完简单工厂模式的实现之后,你和你的小伙伴们肯定会有这样的疑惑(因为我学习的时候也有)——这样我们只是把变化移到了工厂类中罢了,好像没有变化的问题,因为如果工厂想要建造其他形状,此时我们还是需要修改工厂类中的方法(也就是多加case语句,没应用简单工厂模式之前,修改的是客户类)。我首先要说:你和你的小伙伴很对,这个就是简单工厂模式的缺点所在(这个缺点后面介绍的工厂方法可以很好地解决),然而,简单工厂模式与之前的实现也有它的优点:

  • 简单工厂模式解决了客户端直接依赖于具体对象的问题,客户端可以消除直接创建对象的责任,而仅仅是消费产品。简单工厂模式实现了对责任的分割。
  • 简单工厂模式也起到了代码复用的作用,因为之前的实现(形状的类型)中,换了一种类型同样要去在自己的类中实现求周长的方法,然后有了简单工厂之后,建造形状就不需要此时简单工厂的求周长方法就让所有客户共用了。(同时这点也是简单工厂方法的缺点——因为工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都会受到影响,也没什么不好理解的,就如事物都有两面性一样道理)

虽然上面已经介绍了简单工厂模式的缺点,下面还是总结下简单工厂模式的缺点:

  • 工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都会受到影响
  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,这样就会造成工厂逻辑过于复杂。

了解了简单工厂模式之后的优缺点之后,我们之后就可以知道简单工厂的应用场景了:

  • 当工厂类负责创建的对象比较少时可以考虑使用简单工厂模式()
  • 客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时可以考虑使用简单工厂模式

本文主要借鉴了《Gof设计模式》,《Learning hard C#学习笔记》C#设计模式(2)——简单工厂模式