【设计模式】简单工厂模式 Simple Factory Pattern

简单工厂模式Simple Factory Pattern【Simple Factory Pattern】是设计模式里最简单的一个模式,又叫静态工厂模式【Static Factory Pattern】,这个模式没有收录在GOF 23 个模式中,由于他很是简单,在项目中使用也很是普遍,因此就用它来开篇。html

1、简单工厂模式定义:

简单工厂模式(Simple Factory Pattern):定义一个工厂类,它能够根据参数的不一样返回不一样类的实例,被建立的实例一般都具备共同的父类。由于在简单工厂模式中用于建立实例的方法是静态(static)方法,所以简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类建立型模式。设计模式

2、简单工厂模式结构图

image

简单工厂结构图学习

从简单工厂结构图中咱们能够看出它包含三个角色:测试

1. IProduct (抽象产品角色):

它是工厂类所建立的全部对象的父类,封装了各类产品对象的公有方法,它的引入将提升系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,由于全部建立的具体产品对象都是其子类对象。抽象产品可使用接口和抽象类来来实现。编码

2. ConcreteProduct (具体产品角色):

它是简单工厂模式的建立目标,全部被建立的对象都充当这个角色的某个具体类的实例。每个具体产品角色都继承了抽象产品角色,须要实如今抽象产品中声明的抽象方法。在简单工厂模式中,客户端经过工厂类来建立一个产品类的实例,而无须直接使用new关键字来建立对象,它是工厂模式家族中最简单的一员。 在使用简单工厂模式时,首先须要对产品类进行重构,不能设计一个一应俱全的产品类,而需根据实际状况设计一个产品层次结构,将全部产品类公共的代码移至抽象产品类,并在抽象产品类中声明一些抽象方法,以供不一样的具体产品类来实现。spa

3. Factory (工厂角色):

这个角色就是工厂类,他是工厂模式的核心,负责实现建立全部产品实例的内部逻辑;工厂类能够被外界直接调用,建立所需的产品对象;在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product。设计

3、简单工厂的代码实现

public interface IProduct
{
    void SomeMethod();
}

public class ConcreteProductA : IProduct
{
    public void SomeMethod()
    {
        Console.WriteLine("I am product A");
    }
}

public class ConcreteProductB : IProduct
{
    public void SomeMethod()
    {
        Console.WriteLine("I am product B");
    }
}
public class Factory
{
    public static IProduct Create(string productType)
    {
        IProduct product;
        switch (productType.ToUpper())
        {
            case "A":
                product = new ConcreteProductA();
                break;
            case "B":
                product = new ConcreteProductB();
                break;
            default:
                throw new ArgumentException("Invlid Argument.", nameof(productType));
        }
        return product;
    }
}

客户端调用:code

static void Main()
{
    var product = Factory.Create("A");
    product.SomeMethod();

    product = Factory.Create("B");
    product.SomeMethod();

    Console.ReadKey();
}

输出结果:htm

image

 

4、需求改变促使代码重构获得简单工厂模式

甲方提出了这样一个需求:要求作一个音频播放器。只须要支持播放WMA格式的文件就行了。开发人员拿到需求后窃喜,这么简单个需求,简直就是毛毛雨啦。好了编码开始。对象

很快代码就编写好了:

public class Audio
{
    public void Play(string name)
    {
        Console.WriteLine("Start playing...");
        Console.WriteLine($"The song name is: [{name}]");
        Console.WriteLine("..........");
    }
}

[Description("1.2. Simple Factory")]
public class App
{
    static void Main()
    {
        Audio audio=new Audio();
        audio.Play("take me to your heart");

        Console.ReadKey();
    }
}

输出结果以下:

image

甲方看到软件试用一翻感受不错很满意,赶忙给开发人员支付了开发费用。将软件投放市场。

很长一段时间,市场放映不错,这个音频播放器市场占有率提升很快。可是有一天一种新的音频格式诞生了,WAV格式的音频文件愈来愈多了,用户拿到WAV的音乐没办法使用这个播放器播放了,甲方从市场上获得了不少反馈,因而,甲方决定升级这款音频播放器软件使其支持这种新的WAV格式的音频文件。 甲方找到了开发人员要求增长WAV格式文件的播放。开发人员拿到需求后赶忙开始写代码。很快代码写出来了:

public class WmaFile
{
    public void Play(string name)
    {
        Console.WriteLine("Start playing wma file...");
        Console.WriteLine($"The song name is: [{name}]");
        Console.WriteLine("..........");
    }
}

public class WavFile
{
    public void Play(string name)
    {
        Console.WriteLine("Start playing wav...");
        Console.WriteLine($"The song name is: [{name}]");
        Console.WriteLine("..........");
    }
}

[Description("1.2. Simple Factory")]
public class App
{
    static void Main()
    {
        Console.WriteLine("Please input a or m");
        var input = Console.ReadKey();
        if (input != null)
        {
            if (input.Key == ConsoleKey.A)
            {
                WavFile wav = new WavFile();
                wav.Play("take me to your heart.wav");
            }
            else if (input.Key == ConsoleKey.M)
            {
                WmaFile audio = new WmaFile();
                audio.Play("take me to your heart.wma");
            }
        }


        Console.ReadKey();
    }
}

运行软件如图:

image

甲方拿到软件测试一翻,很满意,赶忙投入市场,反响强烈,市场占有率进一步提升。

随着时间的推移一种新的音乐格式出现了,mp3 格式文件的音乐出现了,他的文件占用存储小, 音质好,原来一张光盘只能装5首wma或者wav格式的音乐文件,如今一张光盘能够装30首mp3了。这简直就是一场革命, 可是甲方目前的音频播放播放器不支mp3格式, 甲方的几个竞争对手已经抢先一步支持了mp3的播放,甲方的音频播放器的市场占有率在急剧降低,甲方坐不住了,他来不及从市场上去拿反馈了,赶忙找到开发人员,让开发人员赶忙加班加点开发出支持mp3格式文件,否则他的播放器就要被这个瞬息万变的市场所淘汰。开发人员接到这个命令也是不敢怠慢。赶忙投入工做。

当开发人员打开已经实现的代码时陷入了沉思,再加一个类开发mp3 格式文件的播放功能吗? 若是这样写下去的话代码会变得很难维护和扩展了,如今技术发展这么快,指不定哪一天有冒出一个什么格式,再这样堆积木似的一直写下去本身都不想看本身写的代码了。 因而他决定重构代码,可是怎么重构呢? 忽然脑海中闪过了昨天不是在看《设计模式》吗?里面不是有个简单工厂模式吗?这个模式彷佛很适合中中场景,好的就用这个模式, 把播放音乐的功能提出一个接口, 而后建立出继承这个接口的各个格式文件的具体播放类,而后在建立一个静态工厂根据客户的音乐文件格式选择建立那个具体的类来播放当前的文件。 有了这个思路那就赶忙动手重构代码吧。 很快代码重构完成了:

public interface IAudio
{
    void Play(string name);
}

public class Wma : IAudio
{
    public void Play(string name)
    {
        Console.WriteLine("Start playing wma file...");
        Console.WriteLine($"The song name is: [{name}.wma]");
        Console.WriteLine("..........");
    }
}
public class Wav : IAudio
{
    public void Play(string name)
    {
        Console.WriteLine("Start playing wav file...");
        Console.WriteLine($"The song name is: [{name}.wav]");
        Console.WriteLine("..........");
    }
}
public class Mp3 : IAudio
{
    public void Play(string name)
    {
        Console.WriteLine("Start playing mp3...");
        Console.WriteLine($"The song name is: [{name}.mp3]");
        Console.WriteLine("..........");
    }
}

public class AudioFactory
{
    public static IAudio Create(string songType)
    {
        IAudio audio;
        switch (songType.ToUpper())
        {
            case "A":
                audio = new Wav();
                break;
            case "M":
                audio = new Wma();
                break;
            case "P":
                audio = new Mp3();
                break;
            default:
                throw new ArgumentException("Invalid argument", nameof(songType));
        }

        return audio;
    }
}

[Description("1.2. Simple Factory")]
public class App
{
    static void Main()
    {
        Console.WriteLine("Please input a or m or p");
        var input = Console.ReadKey();
        if (input != null)
        {
            IAudio audio = AudioFactory.Create(input.Key.ToString());
            audio.Play("take me to your hert");
        }

        Console.ReadKey();
    }
}

输出结果:

image

好了这个音频播放器支持mp3了, 甲方很是高兴,赶忙把这个新的版本投入市场。甲方经过此次升级挽回了一些他在音频播放市场上的一些颓势。

经过这么一次忽然的袭击使开发人员收获很多,最起码学到了一种设计模式(simple facfory)的使用,也是成就满满。 可是开发人员仍是心有余悸,若是哪一天忽然有出来个什么新的格式是否是又要加班加点的搞上一阵子。 他想若是在不改变这个软件已有代码的状况下,来扩展,比方说,新增了一个类型,可是不须要修改代码经过写一个类或者增长一些配置就能够实现这个完美支持新的格式的音频文件?(这不是OCP原则吗? ) 那么这个问题Simple Factory Pattern 能作到吗? 问题先留着,随着模式学习的深刻, 让咱们慢慢在后面的学习中去寻找答案。

5、简单工厂模式的优势

  1. 工厂类含有必要的判断逻辑,能够决定在何时建立哪个产品类的实例,客户端能够免除直接建立产品对象的责任,客户程序不须要产品的具体生产细节;简单工厂模式经过这种作法实现了对责任的分割,它提供了专门的工厂类用于建立对象。
  2. 客户端无须知道所建立的具体产品类的类名,只须要知道具体产品类所对应的参数便可,不须要知道产品是怎么建立的。
  3. 经过引入配置文件,能够在不修改任何客户端代码的状况下更换和增长新的具体产品类,在必定程度上提升了系统的灵活性。
  4. 简单工厂将建立产品的职责放在了单独的静态工厂中去处理和维护必定长度上体现了SRP原则

6、简单工厂模式的缺点

  1. 因为工厂类集中了全部产品建立逻辑,一旦不能正常工做,整个系统都要受到影响。
  2. 使用简单工厂模式将会增长系统中具体产品类的个数,在必定程序上增长了系统的复杂度和理解难度。
  3. 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能形成工厂逻辑过于复杂,不利于系统的扩展和维护。
  4. 因为在增长新的产品的时候简单工厂模式必需要修改静态的工厂方法,因此它违背了OCP原则

7、工厂模式的使用场景

在如下状况下可使用简单工厂模式:

  1. 工厂类负责建立的对象比较少:因为建立的对象较少,不会形成工厂方法中的业务逻辑太过复杂。
  2. 客户端只知道传入工厂类的参数,对于如何建立对象不关心:客户端既不须要关心建立细节,甚至连类名都不须要记住,只须要知道类型所对应的参数。
相关文章
相关标签/搜索