C#接口&依赖注入

时间:2014-08-18 13:27:24

标签: c#

我试图更多地了解界面注入以及它们如何挂起。

我的系统有不同类型的用户。每个用户将在主页上看到一组不同的菜单。而不是使用大的switch语句检查用户类型&相应地加载菜单,我创建了一个“User”基类,派生类实现了IMenu接口。但是在Page_Load()上,我仍然需要知道要创建User的类型才能调用LoadMenu()方法。我的问题是,如何摆脱对User对象类型的实例化进行硬编码?或者甚至当我从DB检索数据并创建一种User对象时,我仍然需要检查用户的类型并使用switch。有没有办法摆脱这种情况?

以下是我的代码

//base class
Public class User {
    private string _Username;
    Private string _Name;
}


Public Interface IMenu {
    void LoadMenu();
}

Public class Manager : User, IMenu {
     public override void LoadMenu(){
         //loads manager's menu
     }
}

public class Employee: User, IMenu {
     public override void LoadMenu(){
         //loads employee's menu
     }
}


protected void Page_Load() {
     //Retrieve user details from database
     //Instantiate an object of derived `User` type.
     //Call `LoadMenu()` method.
}

2 个答案:

答案 0 :(得分:1)

如果你想使用依赖注入来加载符合通用接口的不同类型,你应该研究typed factories(至少那是中它的调用方式 - 同样的概念应该在其他框架中找到)

原则如下:你有一个界面,它返回你感兴趣的公共界面

public interface IUserFactory {
    IUser GetUser(string userType);
}

然后,您可以使用区分信息注册从您要解决的公共接口派生的所有类型;它可以是类的类型,也可以是其他一些信息。

最后,您告知工厂已注册的类型与识别信息之间的联系;例如,如果我们使用类类型作为windsor工厂中组件的名称,那么我们可以告诉工厂应该通过使用我们将其作为组件名称传递的参数来解析IUser的请求。在温莎,这可以通过继承DefaultTypedFactoryComponentSelector

来完成
public class CustomTypedFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        if(method.Name == "GetUser" && arguments.Length == 1 && arguments[0] is string)
        {
            return (string)arguments[0];
        }
        return base.GetComponentName(method, arguments);
    }
}

然后你会得到像

这样的东西
var dbUser = DB.LoadUser(1234);
IUser user = IUserFactory.GetUser(dbUser.Type);
user.LoadMenu();

作为一种风格问题,我建议不要让用户负责加载菜单;相反,如果通过将菜单传递给用户来加载菜单,或者甚至是描述授权操作的最佳界面,您可能会处于更好的位置。通过这种方式,您可以为用户加载菜单,但菜单不受它的限制,并且可以加载机器,组等...这是建筑国家,所以我不会再迷路了你的问题是很好的指示:)

答案 1 :(得分:0)

我对你的概念感到有些困惑,避免使用switch语句等同于依赖注入。迟早你需要根据你当前的用户放置应用程序菜单的逻辑。

依赖注入是指尽可能地将您的结构与该决策的逻辑分离,而不是避免它。如果做得好,即使将来逻辑发生变化,您也不需要重写一半代码。

我不知道我是否会按照你的计划进行,但根据你的方法,我会做类似以下的事情(想象得非常快,所以它必然会有漏洞它,但你应该得到这个想法):

public enum UserAccessLevel
{
    None,
    Employee,
    Manager
}

public interface IUser
{
     string Name { get; }
     string UserName { get; }
     UserAccessLevel AccessLevel { get; }
}

public interface IMenuLoader
{
     void LoadMenu()
}

public class MenuLoaderFactory()
{
     public IMenuLoader GetMenuLoader(UserAccesLevel accesLevel)
     {
         IMenuLoader menuLoader = null;

         switch (accesLevel)
         {
             case UserAccessLevel.Employee
                  menuLoader = new EmployeeMenuLoader();
                  break;
             case UserAccessLevel.Manager
                  menuLoader = new ManagerMenuLoader();
         }

         return menuLoader ;   
     }
}

public sealed class EmployeeMenuLoader: IMenuLoader {...}
public sealed class ManagerMenuLoader: IMenuLoader {...}

现在在您的主页面中,您应该只保留对IUser,IMenuLoader的引用。如果在几个月内,您决定添加新的访问级别,则所有管道仍然有效。您只需要创建新的MenuLoader类并更新MenuLoaderFactoryUserLevelAccess枚举中的逻辑(第二个虽然我可能会删除此枚举) )考虑新的访问和你的设置。