Come posso migliorare questo design in modo che non sia necessario eseguire la scansione dinamica delle classi in fase di runtime?

4

Sono nel mezzo di una riprogettazione da parte del mio attuale progetto che riguarda le autorizzazioni e l'autorizzazione degli utenti. Ho un'interfaccia chiamata IUserPermissions che incapsula questa informazione.

Una parte delle autorizzazioni di un utente sono i ruoli di cui sono membri. Sto provando non a violare il principio aperto / chiuso aggiungendo ogni possibile ruolo a IUserPermissions come una proprietà come questa:

interface IUserPermissions {
    bool IsAdmin { get; set; }
    bool IsDeveloper { get; set; }
    // Many more...Yuck.
}

Sento che avere un'interfaccia come questa potrebbe soddisfare meglio l'OCP:

interface IUserPermissions {
    ICollection<IRole> Roles { get; }
    bool IsInRole<T>() where T : IRole;  /* (For convenience; Would probably be
                                             implemented as an extension method) */
}
interface IRole {
    string Name { get; }
}
public class AdminRole : IRole {
    public string Name { get { return "Admin"; } }
}

Dove mi trovo a cadere è come implementare qualcosa come questo. Sto usando l'API dei ruoli ASP.NET come backing store, il che significa che riceverò un string[] di nomi di ruolo per l'utente. La mia implementazione IUserPermissions dovrebbe iniziare con questa raccolta di stringhe e in qualche modo lavorare indietro , quindi istanziare un gruppo di IRole s appropriato per popolare la proprietà Roles .

Questo potrebbe essere fatto scansionando l'assembly per IRoles con un costruttore senza parametri, istanziandoli e leggendo la loro proprietà Name , ma ... ugh. Come posso correggere questo disegno in modo da non avere per fare questo?

    
posta Brant Bobby 11.07.2013 - 22:15
fonte

3 risposte

3

Hai pensato di creare un enum con tutti i possibili ruoli? Certo, se in futuro hai bisogno di un nuovo ruolo da aggiungere, dovrai modificare l'enum, che potrebbe rompere l'OCP, ma onestamente non credo che valga la pena di andare così tanto per evitare qualcosa di così semplice .

P.S .: Inoltre, puoi utilizzare il metodo Enum.Parse per popolare la tua raccolta dalla serie di stringhe.

    
risposta data 11.07.2013 - 23:15
fonte
3

Non conosco .NET buy Mi spiegherò con Java. Puoi vederlo come pseudocodice.

Non devi istanziare ruoli concreti negli implementatori IUserPermissions , né devi avere un elenco di tutti i ruoli o nomi di ruolo.

Aspetto:

package permissions;

import java.util.Collection;

public interface IUserPermissions {
    public Collection<IRole> getRoles(); 
    public void addRole(IRole role);     // add a role to the collection
    public void removeRole(IRole role);  // remove a role from the collection
    public boolean isInRole(IRole role); 
    public boolean isInRole(String roleName);
}

L'implementatore di public boolean isInRole(IRole role); controlla solo se il ruolo è presente nella raccolta utilizzando il metodo constains o simile.

La differenza è qui è questa:

Implementatore di public boolean isInRole(String roleName); attraversa la raccolta confrontando la stringa roleName con il metodo getName() di ciascun ruolo.

Se tutto ciò che hai è un array di nomi di ruolo, usa isInRole(String roleName) , altrimenti usi isInRole(IRole roleName)

Perché funzioni, devi assicurarti che non ci siano due ruoli con lo stesso nome. Puoi farlo con la classe di fabbrica.

Va bene che le classi factory siano accoppiate a lezioni concrete. Il loro lavoro è quello di essere l'unico posto dove viene eseguito "nuovo", quindi il resto del sistema viene disaccoppiato da classi concrete.

    
risposta data 11.07.2013 - 23:05
fonte
2

Perché non codificare il mapping (Role as String - > Role come tipo IRole concreto) in una classe Factory?

public class RoleFactory
{
    public IEnumerable<IRole> CreateRoles(IEnumerable<String> roleStrings)
    {
        return roleStrings.Select(item => this.CreateRole(item)).ToList();
    }

    public IRole CreateRole(String roleString)
    {
        IRole result = null;

        switch(roleString)
        {
            case "Viewer":
                result = new ViewerRole();
                break;

            case "Admin":
                result = new AdminRole();
                break;

            case "Monkey":
                result = new MonkeyRole();
                break;

            default:
                throw new ArgumentException("Unexpected Role String");
        }

        // extra Defensive Programming
        if (result == null)
        {
            throw new InvalidOperationException("Role mapping is broken.");
        }

        return result;
    }
}
    
risposta data 12.07.2013 - 11:12
fonte

Leggi altre domande sui tag