Come al solito, ci sono molti modi diversi. Ecco alcune possibili idee per te da esaminare:
Inheritance
Non lo realizzerei tramite l'ereditarietà, specialmente se non vuoi più ruoli di questo tipo per un membro. Anche con un solo ruolo, c'è il principio composizione sull'ereditarietà che ti dice di evitare di farlo se possibile. Riduce drasticamente il potenziale di riutilizzo. Ad ogni modo, con le tue esigenze che vuoi essere in grado di supportare più ruoli per un membro, devi fornire sottoclassi per ogni combinazione, il che si traduce in un'esplosione esponenziale di combinazioni di classi. Non molto fattibile.
Decorator
Come hai sottolineato, il pattern del decoratore sarebbe un modo possibile, ma richiederebbe ancora molto lavoro nel wrapping del Member
originale e nel fornire tutti i suoi metodi anche attraverso l'oggetto decorato.
Composizione
Composition è un modo semplice che è possibile utilizzare per aggiungere un ruolo a Member
, se è possibile definire un'interfaccia comune per i ruoli, ovvero Supporter
, Volunteer
, ecc. implementerebbe l'interfaccia MemberRole
e la classe Member
ottiene un attributo di tipo MemberRole
(o una lista di essi).
Se, tuttavia, i ruoli hanno metodi diversi, la composizione non funziona più bene (ad esempio un Supporter
ha support()
, ma un Player
ha play()
), perché non puoi trova un'interfaccia comune per i ruoli.
Tratti / mixins
Poiché non ci hai detto quale linguaggio di programmazione vuoi utilizzare, lascia che aggiunga un'altra opzione che sarebbe interessante, ma più difficile da realizzare in alcune lingue. Ad esempio, considera Scala con il suo supporto per i tratti. Sono una soluzione perfetta per il tuo problema, in quanto potresti fare quanto segue:
trait Supporter {
def support = ...
}
trait Player {
def play = ...
}
// and so on
val memberA = new Member with Supporter with Player
memberA.support // works fine
memberA.play // works fine
val memberB = new Member with Supporter
memberB.support // works fine
memberB.play // won't compile
La composizione di tratti come questo ti risparmia essenzialmente l'esplosione esponenziale di cui sopra, poiché non è necessario fornire una classe specifica per un MemberSupporterPlayer e simili. Un ulteriore vantaggio è la sicurezza del tipo mostrata sopra che garantisce che non puoi chiamare metodi da un ruolo che il tuo membro non possiede.
Per qualcosa di simile in C # puoi considerare Mixins. Vedi ad esempio il progetto di remix per questo. Sostanzialmente consentono lo stesso tipo di composizione, ma ovviamente usano una sintassi diversa.