定义

提供一个创建 一系列相关 或相互依赖对象的接口,而无需指定它们具体的类

构成

  • 抽象产品类
  • 具体产品类
  • 抽象工厂类
  • 具体工厂类

调用

  1. 实例具体工厂对象
  2. 使用具体工厂对象创建具体产品

使用场景

  • 适配不同类型的数据库,例如SqlServer Access
  • 创建不同品牌的产品,例如戴尔的电脑,惠普的电脑

代码实现

// 抽象产品
public interface Keyboard
{
    void Input();
}

public interface Mouse
{
    void Click();
}

// 具体产品
public class MacKeyboard : Keyboard
{
    public void Input()
    {
        Console.WriteLine("Mac 专用键盘");
    }
}

public class MacMouse : Mouse
{
    public void Click()
    {
        Console.WriteLine("Mac 专用鼠标");
    }
}

public class WinKeyboard : Keyboard
{
    public void Input()
    {
        Console.WriteLine("Win 专用键盘");
    }
}

public class WinMouse : Mouse
{
    public void Click()
    {
        Console.WriteLine("Win 专用鼠标");
    }
}

// 抽象工厂类
public interface Hardware
{
    Keyboard CreateKeyBoard();
    Mouse CreateMouse();
}

// 具体工厂类
public class MacFactory : Hardware
{
    public Keyboard CreateKeyBoard()
    {
        return new MacKeyboard();
    }

    public Mouse CreateMouse()
    {
        return new MacMouse();
    }

}

public class WinFactory : Hardware
{
    public Keyboard CreateKeyBoard()
    {
        return new WinKeyboard();
    }

    public Mouse CreateMouse()
    {
        return new WinMouse();
    }

}

void Main()
{
    Hardware macFactory = new MacFactory();
    Keyboard macKeyboard = macFactory.CreateKeyBoard();
    macKeyboard.Input();

    Hardware winFactory = new WinFactory();
    Mouse winMouse = winFactory.CreateMouse();
    winMouse.Click();

}

用简单工厂改进抽象工厂(添加判断使用哪种工厂,用户不用判断)<此模式需要在需求改变的情况下改变代码,需要重新编译>

public class DataAccess
{
      private static string db = "Sqlserver"; //指定Sqlserver 数据库,也可指定别的数据库

      public static IUser CreateUser()
     {
           switch()
           {
                case "SqlServer":
                    return new SqlServerUser();
                case "Access":
                    return new AccessUser();
                default:
                    return null;
           }
     }

}

void Main()
{
      IUser user = DataAccess.CreateUser();
      user.Insert(new User());
}

反射 + 配置文件 + 抽象工厂(添加判断使用哪种工厂,用户不用判断)<此模式需要在需求改变的情况下改变配置文件,不需要重新编译>

所有在用简单工厂的地方,都可以考虑用反射技术来去除 switch 和 if, 解除分支判断带来的耦合

App.Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="DB" value="Sqlserver"/>
    </appSettings>
</configuration>
public class DataAccess
{
    private static readonly string AssemblyName = "c15"; //程序集名称
    private static readonly string db = ConfigurationManager.AppSettings["DB"];

    public static IUser CreateUser()
    {
         return (IUser)Assembly.Load(AssemblyName).CreateInstance($"{AssemblyName}.{db}User");
    }

}

优点与缺点

  1. 抽象工厂具有工厂模式的优点,对添加 系列产品符合开闭原则
  2. 抽象工厂添加新产品不符合开闭原则,需要取修改抽象工厂类,添加抽象产品类