Come creare un'interfaccia comune per classi con diversi sottoinsiemi di membri

3

Non so come dirlo, ma cercherò di essere il più chiaro possibile

Ho un progetto in cui sto creando molte classi e quelle classi hanno alcune proprietà e metodi comuni, ma quei metodi potrebbero avere codice diverso in loro,

Quindi ho deciso di creare un'interfaccia in modo che potessi ereditarla in tutte le classi e tutte le classi avrebbero gli stessi metodi in esse come insert, update, delete con codice diverso all'interno di quei metodi, ma questo trucco è fallito quando ho scoperto che non tutte le classi hanno la stessa struttura come alcuni hanno metodo insert ma non update e se erediterò l'interfaccia devo fornire una dichiarazione pubblica per tutti i membri di quell'interfaccia che non voglio.

Trucco due Ho pensato a classi astratte create anch'esse un problema ma identico a tutti i membri astratti che devono essere implementati pubblicamente.

Quindi, per farla breve

Voglio creare una struttura ereditabile comune che funzioni come una stampa blu non istanziata per le classi e possa essere ereditata in molte classi ma in molte classi non voglio implementare tutti i membri di quella struttura

NOTA: Posso fare no-op (metodi vuoti che non fanno nulla, o restituire un null, ad esempio) come alcuni ragazzi mi hanno suggerito, ma questa non è la soluzione che sto cercando perché quei membri non necessari saranno visibili durante l'intelligenza e questo è quello che voglio evitare.

Come posso farlo?

    
posta yogi 15.08.2012 - 19:58
fonte

6 risposte

6

Un modo per farlo è avere un'interfaccia con i tre metodi e implementarla in alcune classi astratte (queste sarebbero le classi base per le implementazioni complete).

Queste classi astratte implementerebbero solo i metodi non necessari e li implementerebbero come no-ops (metodi vuoti che non fanno nulla, o restituiscono null , ad esempio).

Le tue effettive classi di implementazione erediteranno da queste classi astratte e implementeranno solo il sottoinsieme di operazioni necessario.

Un'altra opzione è di aderire a ISP (principio di segregazione dell'interfaccia) e avere un numero di interfacce - una per ogni comportamento necessario.

Aggiornamento:

Questa seconda opzione soddisfa il requisito di non mostrare determinati metodi in IntelliSense. Significa, tuttavia, che non potresti utilizzare un singolo albero ereditario, ma dividerlo (che, dalla tua descrizione del modello, è probabilmente la soluzione corretta).

    
risposta data 15.08.2012 - 20:10
fonte
9

La semplice risposta è creare semplicemente più interfacce: Insertable , Updateable , Deleteable , ecc. Tuttavia, tieni presente che solo perché le classi hanno metodi simili non significa che bisogno per condividere una struttura ereditaria. Si desidera utilizzare l'ereditarietà solo quando è necessario riutilizzare il codice che chiama , come se fosse necessario un contenitore contenente un gruppo di oggetti diversi di tipi diversi e occorre chiamare insert() su tutto in quel contenitore.

    
risposta data 15.08.2012 - 20:16
fonte
0

Quello di cui parli è polimorfismo , che letteralmente significa avere molte forme .

Ad esempio, potresti avere una classe base chiamata Animal , che ha un metodo chiamato Eat(Food food); . Quindi potresti avere tre classi che ereditano da Animal come Dog , Cat e Hen .

Tutte queste sottoclassi dovrebbero avere il metodo Eat (sovrascrivendo il membro della classe base), ma ognuna può avere la sua implementazione (facendo qualcosa di diverso).

Questo può essere ottenuto usando ereditarietà ereditando da una superclasse (classe base), o implementando una o più interfacce (come ha detto @Oded, molte piccole interfacce invece di un'unica grande interfaccia).

Un'altra opzione che mi viene in mente è implementare mixin in C #, ma non sono un professionista. Solo un indizio;).

    
risposta data 15.08.2012 - 20:23
fonte
0

In .net, se un'interfaccia eredita altre interfacce, una classe che implementa le interfacce costitutive può essere considerata come un'implementazione dell'interfaccia combinata semplicemente dichiarandola come una sola, senza richiedere alcuno sforzo aggiuntivo di codifica. Potrebbe quindi essere più comodo avere interfacce IThis , IThat e IThisOrThat che avere un'interfaccia unificata "fa tutto", poiché le classi che possono gestire solo una delle interfacce non devono necessariamente implementare stub per i metodi di interfaccia non possono gestire. Inoltre, il codice che richiede oggetti per gestire entrambe le interfacce in fase di compilazione può richiedere tali oggetti accettando un parametro di tipo IThisOrThat .

Ci sono alcune limitazioni con questo approccio, però:

  1. Anche se può consentire il codice sicuro dal tipo senza typecasting, non esiste alcun modo remoto per specificare che una raccolta contiene oggetti che implementano più interfacce (ad esempio {IThis, IThat}) a meno che tutti i tipi che si desiderano per utilizzare implementare un'interfaccia composita che include tutte le interfacce necessarie. Generics può aiutare molto con parametri e variabili locali, ma può essere meno utile con raccolte di tipo misto.
  2. Il codice può contenere oggetti non solo per sé, ma anche allo scopo di assegnarli ad altri codici. Il codice potrebbe accettare un oggetto che è richiesto per implementare "IThis" e un delegato che deve accettare l'oggetto passato. Se il tipo di parametro dell'oggetto passato è 'IThis', il codice non sarà in grado di passarlo a un delegato che si aspetta un 'IThisOrThat', anche se l'istanza passata implementa di fatto quest'ultima interfaccia. Usare i generici può aiutare qui, ma non sempre rendono le cose totalmente fluide.
  3. A volte il codice potrebbe avere istanze le cui capacità esatte potrebbero variare; in alcuni casi, le istanze potrebbero includere metodi per produrre istanze più efficaci. Ad esempio, una classe immutabile potrebbe avere una funzione 'AsMutable' che restituirebbe un'istanza di classe mutabile inizializzata con i dati da quella immutabile. Potrebbe essere più utile avere campi di tipo "IMaybeMutableFoo", che è implementato da entrambe le classi mutevoli e immutabili e include entrambi i metodi "getter" e "setter", piuttosto che avere i campi di tipo "IReadableFoo" che include un getter ma nessun setter e richiede un typecast ogni volta che si desidera scrivere sul campo anche dopo aver provato che il campo contiene un oggetto mutabile.

Mi piace l'approccio dell'interfaccia composita, ma avere anche interfacce semi-composite (che implementano altre interfacce, ma non sono garantite per farlo in modo utile).

    
risposta data 15.08.2012 - 21:45
fonte
0

Il problema è che tutte le tue classi devono sembrare uguali, perché il programma che le usa non sa (e in realtà non vuoi sapere) quale è quale. I metodi no-op possono essere una buona soluzione a questo problema, ma devono avere senso. Se il programma chiamante ne chiama uno, non dovrebbe essere disturbato quando non fa nulla.

Il passo successivo è quello di avere metodi che restituiscano funzionalità, in modo da non chiamare metodi che un particolare oggetto non può gestire. Se CanFly restituisce false, il chiamante sa di non chiamare FlyToLA . Questo, ovviamente, ti lascia molti metodi non operativi che non dovrebbero mai essere chiamati - una situazione disordinata al massimo.

La soluzione , credo, è che tutte le tue classi / interfacce abbiano un metodo GetFlight che restituisce un'istanza Flight ("Flight" può essere una classe o interfaccia - l'interfaccia potrebbe funzionare meglio). Se quel metodo restituisce null, l'oggetto non vola. Se restituisce un oggetto Flight , quell'oggetto contiene tutti i metodi relativi al volo, quindi sono lì e disponibili se applicabili e inesistenti e inutilizzabili se non lo sono.

E avresti altri metodi, forse piuttosto molti, come GetDrive , GetAccounting , GetPersonell , ecc. Potresti persino annidarli, con l'interfaccia Accounting che ha molto di più "Ottieni" metodi per sottoinsiemi di contabilità. Ci sono molte varianti su questo tema.

    
risposta data 15.08.2012 - 21:00
fonte
-1

Per l'opzione mixin in C #, suggerirei di utilizzare l'approccio della separazione delle preoccupazioni definendo più interfacce. Se si desidera gestire gli oggetti in una singola raccolta, è sufficiente un'interfaccia di base (come IEntity) che abbia solo il 100% di attributi comuni (o nessuno se non ce ne sono). Puoi usare "myObj as IMyInterface" e controllare null per vedere se qualcosa è supportato, e per aggiungere metodi alle interfacce puoi usare i metodi di estensione. I metodi Interfaces + Extension sono un modo C # per ottenere mix-in e ti permettono di definire metodi che agiscono sulle proprietà definite nell'interfaccia ... se hai bisogno che questi facciano parte dell'interfaccia, allora hai semplicemente un'interfaccia IEntityWithMethods che l'implementazione del tuo bambino eredita e mappala esplicitamente ai metodi di estensione.

    
risposta data 20.09.2016 - 16:59
fonte

Leggi altre domande sui tag