mardi 30 novembre 2021

Alternative to enum that is open to extension

I have an IUserRole interface that represents different user role implementations, such as NormalUser and Administrator. Each user user role implementation allows checking for specific permissions that it allows. Example:

public class Program
{
    enum UserPermission { Permission1, Permission2 }
    
    interface IUserRole
    {
        bool HasPermission(UserPermission perm);
    }
    
    class NormalUser : IUserRole
    {
        private static readonly List<UserPermission> _permissions = new() 
        {
            UserPermission.Permission1 
        };
        
        public bool HasPermission(UserPermission perm)
        {
            return _permissions.Any(x => x == perm);
        }
    }
    
    class Administrator : IUserRole
    {
        private static readonly List<UserPermission> _permissions = new() 
        {
            UserPermission.Permission1,
            UserPermission.Permission2
        };
        
        public bool HasPermission(UserPermission perm)
        {
            return _permissions.Any(x => x == perm);
        }
    }
    
    public static void Main()
    {
        IUserRole role = new Administrator();
        var hasPermission = role.HasPermission(UserPermission.Permission1);
        Console.WriteLine($"Has Permission: {hasPermission}");
    }
}

Live example here

I wanted to avoid using enums here because it, to me, violates open/closed principle. The code smell is that the enum is not finite or fixed. Permissions can evolve over time, so I have to ask myself if I want to be adding to the enum each time.

An alternative I considered is this:

public class Program
{
    interface IUserPermission {}
    class Permission1 : IUserPermission {}
    class Permission2 : IUserPermission {}
    
    interface IUserRole
    {
        bool HasPermission<T>() where T : IUserPermission;
    }
    
    class NormalUser : IUserRole
    {
        private static readonly List<IUserPermission> _permissions = new() 
        {
            new Permission1()
        };
        
        public bool HasPermission<T>() where T : IUserPermission
        {
            return _permissions.OfType<T>().Any();
        }
    }
    
    class Administrator : IUserRole
    {
        private static readonly List<IUserPermission> _permissions = new() 
        {
            new Permission1(),
            new Permission2()
        };
        
        public bool HasPermission<T>() where T : IUserPermission
        {
            return _permissions.OfType<T>().Any();
        }
    }
    
    public static void Main()
    {
        IUserRole role = new Administrator();
        var hasPermission = role.HasPermission<Permission1>();
        Console.WriteLine($"Has Permission: {hasPermission}");
    }
}

This sort-of addresses the issue: I now can simply add a class instead of modifying an existing enum when I need to add new permissions. But overall this design is lacking:

  1. I still need to modify the IUserRole implementations to add support for new permissions. This makes any benefits to replacing the enum for something more open to extension nearly moot.
  2. Using empty classes to represent permissions doesn't make me feel warm and fuzzy inside: It feels over engineered and abusive of classes which are classically supposed to represent behavior.

I started this with a specific question: What is the best alternative to enum here as to better respect/adhere to the open/closed principle? But the answer, I suspect, might be broader: The overall design probably needs attention. My whole approach feels wrong somehow.

Live example here

Aucun commentaire:

Enregistrer un commentaire