3

我正在做一个新项目,我正在努力坚持正确的设计方法。我遇到了一个 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 语句将出现在系统的所有部分。如果有办法将它重新分解到一个类中并将它放在一个位置,我不介意保留它,但一遍又一遍地重复它绝对是一种代码味道。我刚刚开始这个项目,所以如果有更好的方法来设计对象层次结构或进行重大更改以消除这个问题,我愿意接受。

4

5 回答 5

4

在替换非面向对象的 switch 语句时,策略模式通常是一个很好的模式

于 2013-04-08T14:49:45.103 回答
3

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

于 2013-04-08T14:48:22.807 回答
3

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

public interface IRole
{
   // whatever you need here
}

public class Administrator : IRole
{
    // your implementation 
}

lostechies.com 上有一篇关于polymorphism-part-2-refactoring-to-polymorphic-behavior的好帖子

于 2013-04-08T14:49:54.647 回答
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 来维护您的事务,而不是处理直接的角色对象 (AdministratorObject),并在您需要调用该类定义中的任何特定方法时将其转换为适当的类。

于 2013-04-08T14:55:54.257 回答
1

在这种情况下,您需要的是工厂模式和策略模式的组合。

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

工厂模式为创建相关或依赖对象系列提供了一个接口,而无需指定它们的具体类。

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

我希望这有帮助。

于 2013-04-08T14:53:01.427 回答