Ci sono quattro possibilità che vedo qui:
Possibilità 1 - NBD
Non è un grosso problema fintanto che esporre Component::Client
non perde gran parte dei dettagli di implementazione. Se ritieni che questo sia il tipo di interfaccia che vuoi esporre e non ti aspetti cambiamenti radicali che potrebbero invalidare questa interfaccia, potrebbe essere semplicemente No Big Deal ™.
Possibilità 2 - Component::Client
s sono principalmente dati
Se l'interfaccia esterna di Component::Client
è per lo più di dati, invece di richiederne uno direttamente, basta chiedere le parti di dati necessarie per crearne una, e basta invece renderla internamente. Se è presente solo una o alcune implementazioni interne fisse di Component::Client
, potresti trovarti in questa situazione. Quindi se il tuo codice chiamante è simile a:
Component::Client client(data, filters, listeners, etc);
component.AddClient(11, client);
// client is never used again
Quindi per quanto riguarda il codice chiamante, il client è solo un archivio dati passato al componente. Anche se il client può avere un carico di comportamento su di esso, se è interessante solo per il componente, allora esternamente è irrilevante e potresti cambiare il metodo in modo che sia:
component.AddClient(11, data, filters, listeners, etc);
// What client?
Il metodo AddClient
crea e memorizza internamente l'istanza Component::Client
, ma il codice esterno non lo saprebbe mai. (Se la creazione di un'istanza Component::Client
è particolarmente complicata, ci sono modi per piegare modelli come un costruttore per quello che potrebbe essere necessario.)
Possibilità 3 - Component::Client
s sono principalmente di comportamento
Se stai passando un'istanza Component::Client
a un'istanza IComponent
principalmente per fornire un comportamento, esponendo Component::Client
potrebbe non essere evitabile. Usarlo come un oggetto strategico è una forma di fornire un comportamento, per esempio. Esistono due modi in cui è possibile che venga fornito un comportamento.
Possibilità 3, parte 1 - Il codice cliente crea un Component::Client
da un numero fisso di implementazioni fornite
Se ci sono alcune implementazioni di Component::Client
che il tuo codice cliente deve scegliere e poi passare, potresti invece passare quel codice client in un enum (o simile). L'enumerazione indica al metodo AddClient
quale tipo di implementazione di Component::Client
creare e archiviare internamente. Nota che sebbene la classe non sia esposta, viene invece esposta una enumerazione.
Possibilità 3, parte 2 - Il codice client estende Component::Client
per fornire implementazioni personalizzate
Qui non puoi fare molto ma esporre Component::Client
. Il codice cliente deve vederlo per estenderlo. Se hai solo bisogno di un po 'di comportamento aggiuntivo per un'implementazione interna altrimenti molto grande, potresti esporre un'interfaccia di strategia molto più semplice affinchè i clienti implementino e nascondano la maggior parte del Component::Client
. Potresti anche riuscire a riutilizzare un'interfaccia preesistente (nella libreria standard o altrove nella tua) per soddisfare le tue esigenze.
Possibilità 4 - Possibilità 2 + Possibilità 3
Se hai un Component::Client
che fornisce sia il comportamento che i dati, puoi guardarlo in due modi. In primo luogo, potrebbe avere senso allora, e dovresti lasciarlo da solo (aka, possibilità 1). In alternativa, è possibile suddividere il comportamento e i dati, quindi fare Possibility 2 e Possibility 3 con le parti appena suddivise. Tieni presente la singola responsabilità mentre fai questo: una classe dovrebbe avere una sola responsabilità, non di più e non di meno.
Come nota finale, voglio sottolineare che in tutti i casi, l'idea di un client componente esiste ancora per il codice client. Spesso, avere una classe o un tipo da rappresentare è utile, anche se solo un valore di dati o un oggetto con valore comportamentale.