autofac / c#:特定基类的通用工厂?

时间:2011-05-29 01:23:39

标签: c# dependency-injection factory autofac

autofac是否可以创建只能解析特定基类类型的通用工厂?

我目前正在看到改造'brownfield'c#项目以使用autofac是多么可行。该项目需要创建一个不同类型但具有相同基类的组件树。这些组件需要身份验证和数据库服务(以及其他内容),因此很难只拥有空的构造函数。

因此,工厂可以创建导出基类的对象,使用autofac扫描这些类,并为它们提供工厂。我认为如果你不必为每个类提供工厂,它可能会更容易构建组件对象树。

顺便说一下,或者这是不好的设计标志?组件和树应该尽可能少,我们将此树传递给其他服务以提供处理和呈现吗?

喜欢(哪里?是神秘工厂)

public MyBase { 
    public Add(MyBase x) {..}
}

public DerivedA: MyBase {}

public DerivedB : MyBase {}

public DerivedC : DerivedA {}

public SomethingElse
{
     private ? _myBaseFact;

     public SomethingElse(? myBaseFact) { 
            _myBaseFact = myBaseFact;
     }

     public BuildComponentTree() {
        DerivedA a = _myBaseFact<DerivedA>();
        DerivedB b = _myBaseFact<DerivedB>();
        DerivedC c = _myBaseFact<DerivedC>();
        a.Add(b);
        b.Add(c);
     }
}

编辑:我被问到一个更具体的例子,这是公平的:)有框架组件,我想允许开发人员创建自己的组件,而无需注册autofac。拥有工厂类意味着开发人员必须将自定义组件添加到此工厂吗?请原谅这个例子,这与我遇到它时框架的工作原理类似。

FwComponent {
    public FwComponent (DataProvider, AuthManager, OtherModule)
    public Add(FwComponent x);
    public Setup();
    public Process();
    public Render();
}

Page : FwComponent {
    public Page(DataProvider, AuthManager, OtherModule): base(..)
    public string Title;
    ...
}

Input: FwComponent {
    public Input(DataProvider, AuthManager, OtherModule): base(..)
    public string Name;
    public int length;
    ...
}

Button: FwComponent {
    public Button(DataProvider, AuthManager, OtherModule): base(..)
    public string Name;
    ...
}

----

MyCustomButton : Button {
     public MyCustomButton(DataProvider, AuthManager, OtherModule): base(..)
}

MyPersonalPage : FwComponent {

    IContext _container;

    public MyPersonalPage(DataProvider, AuthManager, OtherModule, IContext container): base(..)

    public Setup() {
         var input = _container.Resolve<Input>();
         input.Name = 'xxx';
         input.length = 10;
         Add(input);

         var butt = _container.Resolve<MyCustomButton>();
         butt.name = "xxx";
         Add(butt);    
    }

    public Process() {
        DataProvider.DoStuff();
    }
}

2 个答案:

答案 0 :(得分:5)

正如我在对史蒂文的答案的评论中提到的,你的问题似乎是基于对工厂模式和多态性的目的的误解。我会把这些留给他的答案,但让我们对你依赖注入的误解......

1)依赖注入是指服务,而不是实体

目前还不清楚你的组件和树是什么,但我猜它们是你域中的实体,而不是处理它们的服务。依赖注入用于将服务(在最一般意义上)注入到其他服务或粘合层中。示例包括将存储库注入MVC​​控制器,或将Twitter发布Web服务注入视图模型,或将产品工厂注入产品操作服务。

2)依赖注入及其框架与工厂模式无关

虽然它被命名为 AutoFac ,但它不是某种 auto matic fac tory生成器,不仅仅是Unity的一种方式将你所有的课程统一到一个或温莎城堡是......嗯......我不知道。这两个概念可能具有的唯一联系是工厂是您注入的上述服务之一。

3)依赖注入取决于良好的设计

也就是说,您必须使用适当的面向对象的注意事项,比如使用构造函数来构造类。虽然我怀疑树中的组件是实体(如上所述),但如果它们是服务,则Add方法将违背这些设计原则:如果类需要MyBase的实例,则应该将其作为构造函数参数,如果不满足此不变量,则使用适当的保护子句抛出异常。类似地,SomethingElse显然需要一个组件树,可以由根节点指定:那么,它应该将该根节点作为构造函数参数。


换句话说,树状情况的依赖注入友好版本最终会看起来像

interface INotificationService { }
class TwitterNotificationService : INotificationService { }
class FacebookNotificationService : INotificationService { }
class CompositeNotificationService : INotificationService
{
    public CompositeNotificationService(IEnumerable<NotificationService> services) { }
    // implement composite pattern
}

class NotificationController : Controller
{
    public NotificationController(INotificationService service)
    {
        if (service === null)
        {
            throw new ArgumentNullException("service");
        }

        // ...
    }
}

后来,如果你使用穷人的DI,你会做类似

的事情
var controller = new NotificationController(
        new CompositeNotificationService(new []
        {
            new TwitterNotificationService(),
            new FacebookNotificationService()
        }));

DI框架的目的只是将诸如此类的样板构造代码移动到应用程序中的一个中心位置(组合根),从而有效地从应用程序中删除服务构造并将其转换为配置细节。但是你的应用程序需要是DI友好的,这实际上只是意味着符合基本的OO原则,比如使用构造函数,以便DI工作。

答案 1 :(得分:2)

在您提供的代码示例中,您似乎使用_myBaseFact<DerivedA>()_myBaseFact<DerivedB>()请求了特定类型。很难看出你想要完成什么,但这对我来说似乎是一种代码味道,因为你仍然依赖于具体的类型。

正如我所看到的,你有两个选择:要么注入工厂并抽象出派生类型,要么直接将派生类型注入构造函数。使用工厂在两个场景中很有用:

1:您有某种(不是基于类型的)参数,允许工厂识别返回的类型,如以下示例所示:

public class MyBaseFactory : IMyBaseFactory
{
    public MyBase CreateMyBase(IUserContext user)
    {
        // Create a MyBase based on a user object.
    }
}

2:您的工厂有多种工厂方法:

public class MyBaseFactory : IMyBaseFactory
{
    private Autofac.IContainer container;

    public MyBase CreateMyBaseForScenarioA()
    {
        return container.Resolve<DerivedA>();
    }

    public MyBase CreateMyBaseForScenarioB()
    {
        return container.Resolve<DerivedB>();
    }
}

请注意,两个工厂方法都返回MyBase,而不是特定类型。关于多态的整个想法是你不应该关心具体的类型。

IMO无论Autofac是否可以为您创建工厂都无关紧要。您不应该依赖于类似于框架的结构,而是依赖于良好的应用程序设计。如果您采用工厂方法,请为该工厂定义接口并根据该接口注入实例。这使得设计非常干净,并且很好地传达了它的意图。