避免交换机语句的体系结构

时间:2013-04-08 14:45:08

标签: c# architecture solid-principles

我正在开发一个新项目,我正在努力坚持正确的设计方法。我遇到了一个switch语句的问题,我知道这是一个问题,但是我无法以面向对象的方式重新考虑它。

在系统中,用户有0..n个角色。根据用户当前的角色,系统将向该用户返回一组特定数据。用户将能够执行某些操作,但不能执行其他操作等。

public class User
{
    public bool HasRole(string roleName)
    {
        return this.UserRoles.Any(r => r.Name == roleName);
    }

    public string Username { get; set; }

    public long? CurrentRoleId { get; set; }
    public Role CurrentRole { get; set; }

    public virtual IList<Role> UserRoles { get; set; }
}

public class Role
{
    public Role() { }

    public string Name { get; set; }
}

public class GetEventsQuery : IQuery<List<Event>>
{
    public List<Event> Query()
    {
        switch (this.user.CurrentRole.Name)
        {
            case "Administrator":
                UserIsNotInAdministratorRole();
                return repository.All.ToList();

            case "Supervisor":
                UserIsNotInSupervisorRole();
                return repository.All.Where(evnt => evnt.SupervisorId == this.user.RecordId).ToList();

            case "User":
                UserIsNotInUserRole();
                return repository.All.Where(evnt => evnt.UserId == this.user.RecordId).ToList();

            default:
                throw new Exception("GetEventsQuery Unknow exception.");
        }
    }

    private void UserIsNotInUserRole()
    {
        if (!this.user.HasRole("User"))
        {
            throw new NoUserRoleException("User does not have user role!");
        }
    }

    private void UserIsNotInSupervisorRole()
    {
        if (!this.user.HasRole("Supervisor"))
        {
            throw new NoSupervisorRoleException("User does not have supervisor role!");
        }
    }

    private void UserIsNotInAdministratorRole()
    {
        if (!this.user.HasRole("Administrator"))
        {
            throw new NoAdministratorRoleException("User does not have administrator role!");
        }
    }

    public GetEventsQuery(string username, IUserRepository userRepository, IEventRepository repository)
    {
        this.repository = repository;

        var userQuery = new GetUserQuery(username, userRepository);
        this.user = userQuery.Query();
    }

    private readonly User user;
    private readonly IEventRepository repository;
}

此switch语句将显示在系统的所有部分中。如果有一种方法可以将它重新分解为一个类并将它放在一个单独的位置,我不介意保留它,但重复一遍又一遍肯定是代码味道。我刚刚开始这个项目,所以如果有更好的方法来设计对象层次结构或做出重大改变以消除这个问题,我愿意接受它。

5 个答案:

答案 0 :(得分:4)

Strategy Pattern通常是替换非OO switch语句时使用的好模式。

答案 1 :(得分:3)

Role应该是每个角色继承的公共基类(或接口)(例如管理员,主管,用户。)Role的每个实现都应该有关于允许或不允许的信息。对于那个特定的角色。

答案 2 :(得分:3)

您可以使用多态来避免切换吗? 您可以根据需要使用抽象来包含您的行为。然后创建封装特定逻辑的具体实现 - 然后替换你的switch语句。需要重构的是Role,所以可能是

public interface IRole
{
   // whatever you need here
}

public class Administrator : IRole
{
    // your implementation 
}

polymorphism-part-2-refactoring-to-polymorphic-behavior

上有一篇关于lostechies.com的好文章

答案 3 :(得分:2)

接口IRole:

GetRole(); //You can use it to self-define the role
IsUserInRole(Role roleType) //Check if user in specific role
GetList(); //Query

定义特定角色:

SuperVisor implements IRole
{...}

Administrator implements IRole
{...}

此处的优点是,您可以依赖IRole来维护您的事务,而不是处理直接的Role对象(AdministratorObject),并在需要调用该类定义中的任何特定方法时将其强制转换为适当的类。

答案 4 :(得分:1)

在这种情况下,您想要的是FactoryStrategy模式的组合。

策略模式定义了一系列算法,封装了每个算法,并使它们可以互换。策略允许算法独立于使用它的客户端。

Factory模式提供了一个接口,用于创建相关或从属对象的族,而无需指定其具体类。

如果将上述内容一起使用,您将能够创建一个完全可扩展的类,它将提供您想要的所有功能......

我希望这会有所帮助。