2010-06-18 12 views
7

In C#, ho una gerarchia di classi con un paio di classi di base astratte nella parte superiore e un discreto numero di classi derivate. Alcune di queste classi concrete hanno alcune proprietà e metodi comuni che vengono implementati in modo identico. Mi sembra uno spreco e quindi una soluzione potrebbe essere quella di implementare questo comportamento comune in un'altra classe base astratta.Implementare un comportamento comune in alternativa alle classi di base astratte?

abstract class Control; 

abstract class SquareControl: Control 
{ 
    public int SquarishProperty; 
    public void SquarishMethod(); 
}; 

class Window: SquareControl; 
class Button: SquareControl; 

Tuttavia, quello che se diverse altre classi nella gerarchia condiviso qualche altro comportamento, ma anche condividere qualcosa in comune con uno dei controlli di un'altra classe di base? Forse ci sono molte aree di comunanza. Sarebbe impraticabile modellarlo con un'implementazione astratta della classe base, no?

abstract class FlashableControl: Control 
{ 
    public int FlashyProperty; 
    public void FlashMethod(); 
}; 

class StatusBar: FlashableControl; // but it's also a bit square too, hmm... 

Quindi, come si fa a condividere tali implementazioni tra le classi senza utilizzare le classi di base?

Immagino di voler delegare l'implementazione di un'interfaccia a un'altra classe e che tale classe implementa tali proprietà e metodi per conto delle classi desiderate, in modo che all'utente, StatusBar e Window sembrino supportare un'interfaccia standard ma sotto le coperte è qualcos'altro che lo implementa.

Sono in grado di visualizzare le classi di aggregazione che implementano questo comportamento, ma è appropriato e ci sono delle insidie? Quali sono le alternative?

Grazie

+0

FlashMethod, SquarishProperty ecc. Hanno delle implementazioni concrete nella classe base o sono astratte? – SWeko

+0

Qui sono astratti per illustrare ciò che non voglio fare, nell'attuale implementazione ho molte classi derivate alcune delle quali condividono implementazioni identiche, non solo un'interfaccia, ma il codice reale (proprietà e metodi) è identico. – DaBozUK

risposta

3

è possibile utilizzare un modello come questo:

public interface ICommonServices 
{ 
    string SomeProperty { get; set; } 

    void SomeMethod(string param); 
} 

public static class CommonServiceMethods 
{ 
    public static void DoSomething(this ICommonServices services, string param) 
    { 
     services.SomeMethod(services.SomeProperty + ": " + param + " something extra!"); 
    } 
} 

Tutte le classi che implementano ICommonServices ora anche ottenere qualche comportamento gratuitamente tramite il metodo di estensione, che dipende unicamente quelle caratteristiche esposte da tutti ICommonServices esecutori. Se è necessario accedere alle funzionalità della classe base, è possibile inserirla nella propria interfaccia e fare in modo che ICommonServices implementi anche tale interfaccia. Ora è possibile creare funzionalità di estensione 'default' per le interfacce senza dover utilizzare più classi di base.


EDIT

Se volete alcuni di questi metodi da interno, è possibile modificare il modello come questo:

public class MyObject : IServices 
{ 
    public string PublicProperty { get; private set; } 

    string IServices.SomeProperty { get; set; } 

    void IServices.SomeMethod(string param) 
    { 
     //Do something... 
    } 
} 

public interface IPublicServices 
{ 
    string PublicProperty { get; } 
} 

internal interface IServices : IPublicServices 
{ 
    string SomeProperty { get; set; } 

    void SomeMethod(string param); 
} 

internal static class ServiceMethods 
{ 
    public static void DoSomething(this IServices services, string param) 
    { 
     services.SomeMethod(services.SomeProperty + ": " + param + " something extra!"); 
    } 
} 

Fondamentalmente stiamo esponendo le interfacce pubbliche e interne. Si noti che implementiamo i metodi dell'interfaccia interna in modo esplicito, in modo che i metodi non siano disponibili per il consumo pubblico (poiché il client pubblico non può accedere al tipo di interfaccia.) In questo caso, i metodi di estensione helper sono interni, basandosi sul interfaccia interna, anche se potresti anche creare metodi di supporto pubblico che si basano sull'interfaccia pubblica.

+0

Anche i contenitori Microsoft Unity IoC utilizzano questo trucco. –

+0

Questo approccio mi piace molto e vorrò realizzarlo. Qual è l'approccio migliore per nascondere questa classe dal codice client in altri assembly? Boz – DaBozUK

+0

@DaBozUK, ho modificato la risposta con un esempio su come fornire solo l'accesso interno –

3

Si potrebbe usare 'ha-un' invece di 'is-a' e delegare a un controllo piazza interna

class Window : Control, ISquareControl 
    { 
     private SquareControl square; 

     public void SquareOperation() 
     { 
      square.SquareOperation(); 
     } 
    } 

    class SquareControl : Control, ISquareControl 
    { 
     public void SquareOperation() 
     { 
      // ... 
     } 
    } 
+0

Questo è più o meno quello che avevo in mente con l'approccio di aggregazione, e credo che potrei voler mantenere la classe SquareControl interna in modo che sia nascosta agli utenti di Window. Ci sono delle insidie ​​con questo approccio, limitazioni o trucchi? – DaBozUK

0

Un modo è quello di utilizzare interfacce e classi di base.

Flashable sarebbe una buona interfaccia anziché una classe.

+0

L'interfaccia e la classe base è praticamente ciò che ho descritto sopra che volevo evitare. Il problema è che la gerarchia delle classi può diventare disordinata se diversi insiemi di classi condividono alcuni ma non tutti i comportamenti. – DaBozUK

Problemi correlati