【C# · 设计模式 · 创建型模式】工厂方法 / 抽象工厂

“工厂”主要是为了避免类与类之间直接使用new实例化对象造成的高耦合,让工厂代替我们去创建对象,形成一个中介的角色,实现新功能时避免对已有代码进行修改。

工厂模式重点是封装变化的部分,哪里需要变化才封装哪里。它适用于对象的结构相对稳定,但系列或风格易发生扩展的情况,一定要找到变化点来运用设计模式。

以咖啡店为例子逐步实现简单工厂工厂方法抽象工厂。

目录

1. 没有使用任何设计模式的程序案例

2.简单工厂模式

3.工厂方法模式

4.抽象工厂模式 


1. 没有使用任何设计模式的程序案例

各个类实现的功能:

程序类实例化一个咖啡店,并通过Order方法点了一杯美式咖啡;

咖啡店类会根据传入的string判断创建什么咖啡实例返回;

抽象咖啡类下有两个子类:美式咖啡和拿铁咖啡。

存在的问题:

如果需要加一个生椰拿铁咖啡,就必须修改咖啡商店的代码增加一条判断,违背开闭原则。而且如果咖啡店开了1000家,意味着需要在1000个不同的地方修改代码。

// 客户程序本身
namespace Factory_Cofe_Before
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            CoffeeStore store = new CoffeeStore();
            store.OrderCoffee("American");
        }
    }
}
// 咖啡店
namespace Factory_Cofe_Before
{
    public class CoffeeStore
    {
        public Coffee OrderCoffee(string type)
        {
            Coffee coffee = null;
            if ("American".Equals(type))
            {
                coffee = new AmericanCoffee();
            }
            else if ("Latter".Equals(type))
            {
                coffee = new LatterCoffee();
            }
            if (coffee != null)
            {
                coffee.AddMilk();
                coffee.AddSugar();
            }
            return coffee;
        }
    }
}
using System;
namespace Factory_Cofe_Before
{
    // 抽象咖啡类
    public abstract class Coffee
    {
        public abstract string GetName();
        
        public void AddSugar()
        {
            Console.WriteLine("加糖");
        }

        public void AddMilk()
        {
            Console.WriteLine("加奶");
        }
    }

    // 拿铁咖啡 : 抽象咖啡类
    public class LatterCoffee : Coffee
    {
        public override string GetName()
        {
            return "拿铁咖啡";
        }
    }

    // 美式咖啡 : 抽象咖啡类
    public class AmericanCoffee : Coffee
    {
        public override string GetName()
        {
            return "美式咖啡";
        }
    }
}

2.简单工厂模式

简单工厂的修改方案:

其实这部分代码在所有的咖啡店内都需要增加相同的判断,那干脆把这部分抽出来变成一个工厂类,让其他所有的咖啡店去调用这个工厂类,具体逻辑也在工厂类中实现。如果此时再需要增加一个生椰拿铁,只需要修改工厂类中的判断,避免大量脚本的修改。

存在的问题:

依旧违背开闭原则,仍需要修改已有的脚本。

// 简单工厂的咖啡工厂
namespace Factory_Cofe_SimpleFactory
{
    public class SimpleCoffeeFactory
    {
        public Coffee CreateCoffee(string type)
        {
            Coffee coffee = null;
            if ("American".Equals(type))
            {
                coffee = new AmericanCoffee();
            }
            else if ("Latter".Equals(type))
            {
                coffee = new LatterCoffee();
            }
            return coffee;
        }
    }
}
// 拥有咖啡工厂后,商店的功能更加简单
// 只需要让工厂根据传入的咖啡名来实例化一个咖啡
namespace Factory_Cofe_SimpleFactory
{
    public class CoffeeStore
    {
        public Coffee OrderCoffee(string type)
        {
            SimpleCoffeeFactory factory = new SimpleCoffeeFactory();

            Coffee coffee = factory.CreateCoffee(type);
            
            coffee.AddMilk();
            coffee.AddSugar();

            return coffee;
        }
    }
}

3.工厂方法模式

工厂方法模式的修改方案:

由于简单工厂在增加新咖啡品种时,还是需要修改原先类中的代码,违背了开闭原则,对这部分进行提取。

新增抽象工厂接口,并实现美式咖啡工厂和拿铁咖啡工厂,通过具体的咖啡工厂实例化具体的咖啡。

商店类中传入具体的咖啡工厂类,并通过这个工厂类创建的咖啡交付给用户。

这样以后如果需要新的生椰拿铁类,只需要新建一个具体的生椰拿铁工厂类,之后给咖啡商店类传入生椰拿铁工厂即可,原先类的代码都不需要进行修改,从工厂方法模式开始真正遵守开闭原则。

工厂方法的问题:如果有100种咖啡,然后又有100种蛋糕,100种盘子....意味着有300个具体的工厂,可能会导致类的爆炸。

// 首先需要根据需要的咖啡,实例化一个工厂,然后将这个工厂传入商店中
namespace Factory_Cofe_FactoryMethod
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            CoffeeStore store = new CoffeeStore();

            ICoffeeFactory factory = new AmericanCoffeeFactory();

            store.SetFactory(factory);
            
            store.OrderCoffee();
        }
    }
}
// 修改后的咖啡商店,需要传入一个咖啡工厂,根据传入的咖啡工厂制作咖啡并返回
namespace Factory_Cofe_FactoryMethod
{
    public class CoffeeStore
    {
        private ICoffeeFactory factory;

        public void SetFactory(ICoffeeFactory coffeeFactory)
        {
            this.factory = coffeeFactory;
        }
        
        public Coffee OrderCoffee()
        {
            Coffee coffee = factory.CreateCoffee();
            
            coffee.AddMilk();
            coffee.AddMilk();
            
            return coffee;
        }
    }
}
namespace Factory_Cofe_FactoryMethod
{
    // 接口:咖啡工厂
    public interface ICoffeeFactory
    {
        Coffee CreateCoffee();
    }

    // 美式咖啡工厂类,生产美式咖啡
    public class AmericanCoffeeFactory : ICoffeeFactory
    {
        public Coffee CreateCoffee()
        {
            return new AmericanCoffee();
        }
    }

    // 拿铁咖啡工厂类,,生产拿铁咖啡
    public class LatterCoffeeFactory :ICoffeeFactory
    {
        public Coffee CreateCoffee()
        {
            return new LatterCoffee();
        }
    }
}

4.抽象工厂模式 

抽象工厂方法的方案:如果这些咖啡、蛋糕之间有一定的套餐关系,或者可以理解为,一个套餐内有一杯咖啡和一个甜点的情况是相对稳定的,但这杯咖啡是什么,甜点是什么的变化是较丰富的。可以考虑把同种套餐的产品都放进一个工厂内,也就是抽象工厂拥有制造蛋糕和咖啡的抽象方法。

具体工厂A可以生产美式咖啡、美式甜品,B可以生产英式咖啡、英式甜品。重点在于它的生产结构没有发生变化,都是咖啡+甜品的结构,只是系列发生了扩展。

存在的问题:如果结构发生了扩展,所有工厂都需要发生扩展。实际上也不能称之为缺点,只是抽象工厂的特点是用于解决系列扩展的问题。

下面用一个游戏上的例子,更能描述这个问题。游戏场景是相对稳定的,比如这里就是有一个房屋和两条道路,但他可能是一个罗马的房屋、美式的房屋、中式的房屋....

Program 给 GameManager 传入一个游戏风格场景的工厂

GameManager 根据传入的具体风格工厂创建道路、房屋等场景,并提供Play展示方法

FacilityFactory 抽象工厂

OldFactoryModernFactory 古代风格工厂,现代风格工厂

// 程序主体,会根据现代风格或者古代风格去生成一张地图
namespace Creational_AbstractFactory
{
    class Program
    {
        public static void Main()
        {
            GameManager g = new GameManager(new ModernFacilityFactory());
            g.BuildGameFacilities();
            g.Play();

            GameManager g2 = new GameManager(new OldFacilityFactory());
            g2.BuildGameFacilities();
            g2.Play();
        }
    }
}
// GameManager 需要传入一个具体的场景建造工厂
// 然后通过Build方法去实例化道路等场景,最后通过Play展现出场景
namespace Creational_AbstractFactory
{
    public class GameManager
    {
        FacilityFactory _facilityFactory;
        private Road road;
        private Building building;
        private Tunnel tunnel;
        private Jungle jungle;

        public GameManager(FacilityFactory facilityFactory)
        {
            this._facilityFactory = facilityFactory;
        }

        public void BuildGameFacilities()
        {
            road = _facilityFactory.CreateRoad();
            building = _facilityFactory.CreateBuilding();
            tunnel = _facilityFactory.CreateTunnel();
            jungle = _facilityFactory.CreateJungle();
        }

        public void Play()
        {
            road.Log();
            building.Log();
            tunnel.Log();
            jungle.Log();
        }
    }
}
namespace Creational_AbstractFactory
{
    // 抽象道路
    public abstract class Road
    {
        public abstract void Log();
    }

    // 抽象房屋
    public abstract class Building
    {
        public abstract void Log();
    }

    // 抽象地道
    public abstract class Tunnel
    {
        public abstract void Log();
    }

    // 抽象丛林
    public abstract class Jungle
    {
        public abstract void Log();
    }

    // 抽象工厂
    public abstract class FacilityFactory
    {
        public abstract Road CreateRoad();
        public abstract Building CreateBuilding();
        public abstract Tunnel CreateTunnel();
        public abstract Jungle CreateJungle();
    }

}

// 现代风格工厂
namespace Creational_AbstractFactory
{
    public class ModernRoad : Road
    {
        public override void Log() => Console.WriteLine("现代化道路");
    }

    public class ModernBuilding : Building
    {
        public override void Log() => Console.WriteLine("现代化建筑");
    }

    public class ModernTunnel : Tunnel
    {
        public override void Log() => Console.WriteLine("现代化地道");
    }

    public class ModernJungle : Jungle
    {
        public override void Log() => Console.WriteLine("现代化丛林");
    }

    public class ModernFacilityFactory : FacilityFactory
    {
        public override Road CreateRoad()
        {
            return new ModernRoad();
        }

        public override Building CreateBuilding()
        {
            return new ModernBuilding();
        }

        public override Tunnel CreateTunnel()
        {
            return new ModernTunnel();
        }

        public override Jungle CreateJungle()
        {
            return new ModernJungle();
        }
    }
}

// 古代风格工厂
namespace Creational_AbstractFactory
{
    public class OldRoad : Road
    {
        public override void Log() => Console.WriteLine("古老版道路");
    }

    public class OldBuilding : Building
    {
        public override void Log() => Console.WriteLine("古老版建筑");
    }

    public class OldTunnel : Tunnel
    {
        public override void Log() => Console.WriteLine("古老版地道");
    }

    public class OldJungle : Jungle
    {
        public override void Log() => Console.WriteLine("古老版丛林");
    }

    public class OldFacilityFactory : FacilityFactory
    {
        public override Road CreateRoad()
        {
            return new OldRoad();
        }

        public override Building CreateBuilding()
        {
            return new OldBuilding();
        }

        public override Tunnel CreateTunnel()
        {
            return new OldTunnel();
        }

        public override Jungle CreateJungle()
        {
            return new OldJungle();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/MeTicals/article/details/126163157
今日推荐