65.9K
CodeProject 正在变化。 阅读更多。
Home

抽象工厂管理器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.64/5 (8投票s)

2005年9月17日

4分钟阅读

viewsIcon

40101

downloadIcon

133

本文详细介绍了如何为抽象工厂模式创建一个可重用的管理类。

引言

抽象工厂提供了一个接口,用于创建相关的依赖对象族,而无需指定它们的具体类。假设您有大量具体工厂,并且用户可以在运行时选择他们希望使用的具体产品类型。您需要一种有效的方法来确定要使用的正确工厂,以便创建所需的相关类型。本文介绍了如何开发一个小型实用程序,使用抽象工厂模式和自定义属性来管理对象的创建。

背景

背景

开发此代码的原因是为了消除或至少显着减少在项目中添加新的具体工厂类时需要完成的编码量。我还需要一种方法,让用户可以选择他们希望创建的类型,而无需知道如何创建特定对象。我们如何确定哪个工厂创建了用户选择的类型?当然!一个巨大的 switch 语句... 不,那根本不行,那太冗长了,并且需要为每个新工厂添加一个额外的 case 语句。我们真正需要的是一个用于工厂的自定义属性,它告诉我们它们可以创建的类型。是的,那会很棒。

接口

考虑一个由具有相应控件的数据对象组成的系统。此类系统的抽象工厂可能如下所示

public interface IBeerFactory
{
    IBeerIngredient    CreateIngredient();
    IBeerControl    CreateControl();
}

现在我们知道所有实现 IBeerFactory 的类型都必须同时创建 Controls 和 Ingredients,我们最好也定义这些

public interface IBeerIngredient
{
    string Name { get;set; }
    double Amount { get;set; }
    string Description { get;set; }
}

public interface IBeerControl
{
    void UpdateControl( IBeerIngredient ingr );
    void UpdateIngredient( IBeerIngredient ingr );
}

代码

当然,我们需要继续开发一些具体工厂、配料和控件。例如,可以有一组用于啤酒花、大麦和酵母的类。为了简洁起见,我将展示 Hops 配料、控件和工厂的代码。然后我们将准备好开始 Factory Manager 的工作。

// Data Object that stores info about the deliciously 

// bitter hops plant.

public class Hops : IBeerIngredient
{
    private string mName;
    private string mDesc;
    private double mAmt;

    internal Hops()
    {
        mName = ""; 
        mDesc = "";
        mAmt = 0.0;
    }

    public string Name
    {
        get
        {
            return mName;
        }
        set
        {
            mName = value;
        }
    }

    public double Amount
    {
        get
        {
            return mAmt;
        }
        set
        {
            mAmt = value;
        }
    }

    public string Description
    {
        get
        {
            return mDesc;
        }
        set
        {
            mDesc = value;
        }
    }
}

// The user control so users can control the hops object.

// Seems a little redundant?!

public class HopsControl : IBeerControl, UserControl
{
    private TextBox txtName;
    private TextBox txtDesc;
    private TextBox txtAmt;
    
    internal HopsControl()
    {
        Initialize();
    }

    private void Initialize()
    {
        txtName = new TextBox();
        txtName.Parent = this;

        txtDesc = new TextBox();
        txtDesc.Parent = this;

        txtAmt = new TextBox();
        txtAmt.Parent = this;
    }

    public void UpdateControl(IBeerIngredient ingr)
    {
        // Make sure the Ingredient is indeed hops

        Hops hops = ingr as Hops;
        if( hops != null )
        {
            // Set the controls

            txtName.Text = hops.Name;
            txtDesc.Text = hops.Description;
            txtAmt.Text = hops.Amount;
        }
    }

    public void UpdateIngredient(IBeerIngredient ingr)
    {
        // Once again make sure the Ingredient is hops

        Hops hops = ingr as Hops;
        if( hops != null )
        {
            // Set the ingredient

            hops.Name = txtName.Text;
            hops.Description = txtDesc.Text;
            hops.Amount = Convert.ToDouble( txtAmt.Text );
        }
    }
}

// A simple little class to return new objects

public class HopsFactory : IBeerFactory
{
    public IBeerIngredient CreateIngredient()
    {
        return new Hops();
    }

    public IBeerControl CreateControl()
    {
        return new HopsControl();
    }
}

好的,这很简单。现在我们将转向自定义属性,以便我们的工厂管理器能够确定具体工厂可以创建的内容。我们绝对需要一些通用的东西,因为我也很懒,永远不想再次重现此代码。我想到的一个属性是存储工厂可以创建的所有类型。让我们称之为 CreatesAttribute。像任何自定义属性一样,它必须从 System.Attribute 继承。

public class CreatesAttribute : System.Attribute
{
    private Type [] mCreatableTypes;
}

自定义属性还需要告诉编译器如何使用它,以便将正确的元数据发出到创建的程序集中。这是通过使用 AttributeUsageAttribute 完成的。

[AttributeUsage( AttributeTargets.Class, Inherited=false, AllowMultiple=true )]
public class CreatesAttribute : System.Attribute
{
    private Type [] mCreatableTypes;
}

因此,我们的自定义属性可以放在类上,它不会被任何子类继承,并且我们将允许为工厂指定多个 CreatesAttribute。听起来不错? ...酷!

毫无疑问,我们需要一些构造函数和一种访问可创建类型的方法。创建两个构造函数怎么样,一个接受类型数组,另一个接受单个类型。至于访问,让我们只做一个简单的小 get 属性。

[AttributeUsage( AttributeTargets.Class, Inherited=false, AllowMultiple=true )]
public class CreatesAttribute : System.Attribute
{
    private Type [] mCreatableTypes;
    
    // The Ctor taking a single type

    public CreatesAttribute( Type type )
    {
        mCreatableTypes = new Type [] { type };
    }
    
    // The Ctor taking a list of types

    public CreatesAttribute( Type [] types )
    {
        mCreatableTypes = types;
    }
    
    // The way to access the creatable types.

    public Type [] CreatableTypes
    {
        get
        {
            return mCreatableTypes;
        }
    }
}

不要忘记使用我们的新自定义属性标记具体工厂。使用以下任一方式

[Creates( new type [] { typeof(Hops), typeof(HopsControl) } )]
public class HopsFactory : IBeerFactory

[Creates( typeof(Hops) )]
[Creates( typeof(HopsControl) )]
public class HopsFactory : IBeerFactory

好的,好的,我知道我一直在喋喋不休,让我们开始做点好事吧。FactoryManager 是一个类,它将管理检索实现 IBeerFactory 接口的具体类。但这还不够好。让我们设计它,以便它可以管理我们将创建的任何可能的工厂接口。

using System;
using System.Collections;
using System.Reflection;

public sealed class FactoryManager
{
    private Type mFactoryInterface;  // The interface to manage

    private SortedList mFactories;   // The factories

    
    public FactoryManager( Type factoryInterface )
    {
        mFactoryInterface = factoryInterface;
        mFactories = new SortedList();
    }

您可能想知道我们将如何检索我们所有的具体工厂并将它们放入排序列表中。嗯,让我们稍微反思一下(双关语,哈哈,很烂)。因为我们可能不知道所有工厂存在的位置,所以让我们通过让他们加载适当的程序集来将这个选择交给开发人员。

    public void LoadFactoriesFromAssembly( Assembly assembly )
    {
        Type [] allTypes = assembly.GetTypes();
       
       //Search through all the types in the assembly

        foreach( Type type in allTypes )
        {
            Type intrface = type.GetInterface(mFactoryInterface.FullName);
            // Look for classes implementing the factory interface

            if( intrface != null )
            {
                // Get an array containing the CreatesAttributes

                object [] attribs = 
                  type.GetCustomAttributes( typeof(CreatesAttribute), false );
                object factory;
                
                // If any CreatesAttributes were defined

                if( attribs.Length != 0 )
                {
                    // Create an instance of the factory

                    factory = assembly.CreateInstance( type.FullName );
                    
                    // Loop through each custom attribute

                    // because multiple Creates

                    // may have been defined.

                    foreach( CreatesAttribute createsAttrib in attribs )
                    {
                        // Get an array of creatable types.

                        Type [] createsTypes = createsAttrib.CreatableTypes;
                        
                        // Store the pair in the sorted list

                        // by using the creatable Types

                        // full name as the key and the instance

                        // of the factory as the value.

                        foreach( Type creatable in createsTypes )
                        {
                            mFactories[creatable.FullName] = factory;
                        }
                    }
                }
            }
        }
    }

哇!这很酷,不是吗?好吧,反正我是这么认为的。当然,我们最好完成几个允许我们获取工厂的函数。

    public object GetFactory( string fullname )
    {
        // Make sure a factory exists that can create the desired type

        if( mFactories.ContainsKey( fullname )
            return mFactories[fullname];
        
        return null;
    }
    
    public object GetFactory( Type type )
    {
        return GetFactory( type.FullName );
    }
}

最后,我将展示一个 FactoryManager 工作的简单示例。

public class Example
{
    public static void Main()
    {
        FactoryManager mgr = new FactoryManager( typeof(IBeerFactory) );

        // Just load this assembly

        Console.WriteLine( "Loading Assembly: AbstractFactoryManager");
        mgr.LoadFactoriesFromAssembly( mgr.GetType().Assembly );

        // Get a factory using the type of hops

        Console.WriteLine( "Retreiving a HopsFactory");
        IBeerFactory factory = (IBeerFactory)mgr.GetFactory( typeof(Hops) );
        Console.WriteLine( "IBeerFactory factory is of type {0}\n", 
                                               factory.GetType() );

        // Create an ingredient and a control

        Console.WriteLine( "Creating an Ingredient.");
        IBeerIngredient ingr = factory.CreateIngredient();
        Console.WriteLine( "Creating a Control.");
        IBeerControl ingrCtrl = factory.CreateControl();

        // Let's display the types to see if it worked.

        Console.WriteLine( "IBeerIngredient ingr is of type {0}", 
                                                ingr.GetType() );
        Console.WriteLine( "IBeerControl ingrCtrl is of type {0}\n\n", 
                                                 ingrCtrl.GetType() );


        // Get a factory using the fully qualified name of a yeast control

        Console.WriteLine( "Retrieving a YeastFactory");
        factory = (IBeerFactory)
           mgr.GetFactory( "AbstractFactoryManager.YeastControl" );
        Console.WriteLine( "IBeerFactory factory is of type {0}\n", 
                                               factory.GetType() );

        // Create an ingredient and a control

        Console.WriteLine( "Creating an Ingredient.");
        ingr = factory.CreateIngredient();
        Console.WriteLine( "Creating a Control.");
        ingrCtrl = factory.CreateControl();

        // Let's display the types to see if it worked.

        Console.WriteLine( "IBeerIngredient ingr is of type {0}", 
                                                ingr.GetType() );
        Console.WriteLine( "IBeerControl ingrCtrl is of type {0}\n", 
                                               ingrCtrl.GetType() );
    }
}

Abstract Factory Manager Example Output

关注点

FactoryManager 类非常有帮助,可以在任何需要抽象工厂设计模式的项目中使用。我希望这能帮助一些开发人员节省一些时间。尽情享受吧!

© . All rights reserved.