Qual è lo schema giusto da usare qui

7

scenario:

Ho un'entità chiamata Member . I membri cadono in più tipi. Alcuni tipi sono di seguito.

  • Supporter
  • Volunteer
  • Sponsor
  • Player

Posso creare una classe per ogni tipo ed ereditare dall'entità Member di base. Ma quando Member ha più di 1 tipo in un dato tempo, non è la strada giusta.

posso utilizzare il modello decoratore per raggiungere questo scopo? O quale è lo schema migliore per ottenere questo lavoro?

EDIT:

La lingua che uso è C #

Anche diversi tipi di membri possono avere metodi aggiuntivi, ad esempio Sponsor può avere i propri metodi come Donate() , oppure Volunteer può avere un metodo Subscribe() e così via. queste cose devono essere considerate.

    
posta Muneer 05.02.2013 - 06:58
fonte

4 risposte

9

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.

    
risposta data 05.02.2013 - 07:22
fonte
3

Anche con metodi univoci in ogni ruolo, puoi usare Composition. Puoi utilizzare Member che ha più ruoli e utilizzare Visitor pattern per accedere ai loro metodi unici.

    
risposta data 05.02.2013 - 09:09
fonte
3

Non utilizzerei assolutamente un pattern di decorazione, perché non riesco a vedere come corrisponda al modello di dominio.

Ma qual è il dominio? Sembra che si tratti di un elenco di membri di un club sportivo o simili. Perché tieni traccia di quali tipi di membri? È puramente per mantenere le informazioni in un database membro, o usi effettivamente tali informazioni in qualche tipo di calcolo?

Se non stai effettivamente utilizzando le informazioni, allora non hai bisogno di alcun tipo di approccio polimorfico. Dovresti creare semplici proprietà booleane sulla classe Member , IsSupporter, IsVolunteer, ecc.

Tuttavia, se effettivamente lo usi per qualcosa, potresti aver bisogno di un approccio polimorfico. Penso che il fatto che un membro possa essere di diversi tipi indica che esiste un concetto di dominio nascosto, ad esempio Role .

Un Member dovrebbe quindi avere più ruoli. Un metodo Member , la cui implementazione dipenderebbe dal ruolo, dovrebbe probabilmente essere implementato effettuando una chiamata simile a tutti i ruoli nel membro.

Ma ulteriori informazioni sul dominio potrebbero portare a una risposta migliore.

    
risposta data 05.02.2013 - 15:27
fonte
1

Vorrei mettere in dubbio la necessità di richiedere una singola classe "Membro" (e una sola) per rappresentare ogni persona in modo univoco. È probabile (o anche possibile) che un membro agisca come volontario e sponsor e importa che siano la stessa istanza dell'oggetto nel codice? Nell'esempio che stai utilizzando, è perfettamente legittimo avere un'istanza di un volontario e un'istanza separata di uno sponsor, anche se sono la stessa persona nella vita reale.

Inoltre, tu dici che i diversi ruoli hanno nomi / funzionalità diversi. Pertanto, affinché il principio di sostituzione di Liskov si applichi, il codice usando chiamerebbe solo metodi sull'interfaccia "Membro". Quindi, la classe Member dovrebbe avere un nome generico come "DoYourBehavior" e la classe derivata sa chiamare "Play / Donate" o l'ereditarietà non si applica realmente; diverso da quello per ottenere il riutilizzo del codice comune.

Quindi, tratterei ogni ruolo come istanza di una classe istanziata separatamente e qualsiasi funzionalità comune può essere condivisa per ereditarietà, composizione o nessuno dei due. La chiave è che non c'è motivo per cui il design debba esplicitamente passare una singola istanza di classe tra diversi ruoli.

    
risposta data 06.02.2013 - 17:26
fonte

Leggi altre domande sui tag