Problema relativo al principio ASCIUTTO e al polimorfismo

0

Ho una classe che è identica al 99% alla sua controparte. Il codice di esempio va sotto:

public abstract class BaseClass
{
    void Method1() { }
    void Method2() { }
    void Method3() { }
    public void MainMethod(List<Object1> listObject)
    {
        Method1();
        Method2();
        Method3();
        Method4();
    }

    abstract internal void Method4();

}

public sealed class DerivedClass : BaseClass
{
    internal override void Method4()
    {
        throw new NotImplementedException();
    }
}

public class BaseClass2
{
    void Method1() { }
    void Method2() { }
    void Method3() { }
    public void MainMethod2(List<Object2> listObject)
    {
        Method1();
        Method2();
        Method3();
    }

}

Come puoi vedere, BaseClass e BaseClass2 sono quasi identici a Metodo1 ... Metodo3. Esiste un'alternativa migliore che può essere utilizzata per minimizzare la duplicazione del codice.

Inizialmente ho iniziato questo polimorfismo come materiale incoraggiante, ho oltre 11 classi derivate su BaseClass e, a causa della seconda implementazione di MainMethod2, sono bloccato con la duplicazione del codice.

Non riesco a pensare a un modo intelligente per uscirne.

Aggiornamento: Quindi questa domanda potenzialmente contiene due parti:

1. Come evitare la duplicazione del codice? Ho risolto questo problema usando la composizione con successo come suggerito dalle meravigliose risposte di seguito.

2. Come evitare astrazioni leaky ? Con questo sto ancora lottando ...

    
posta shankbond 21.11.2013 - 18:41
fonte

4 risposte

1

Vedo due opzioni.

  1. Modifica BaseClass per contenere un'istanza di BaseClass2 . Cambia BaseClass.MainMethod per invocare BaseClass2.MainMethod (invece della sua implementazione dei metodi 1-3).

  2. Refactor Method1 , Method2 e Method3 in una nuova classe. Modifica BaseClass e BaseClass2 per usare la nuova classe (invece della loro implementazione dei metodi 1-3).

risposta data 21.11.2013 - 19:03
fonte
1

La cosa più semplice è sbarazzarsi di BaseClass2. Le sottoclassi dovrebbero semplicemente estendere BaseClass e sovrascrivere Method4 con un metodo che non fa nulla.

In caso contrario, basare BaseClass2 su BaseClass. Tutto ciò che serve è MainMethod2.

Potresti fare un ulteriore passo avanti e sbarazzarti di MainMethod2 e sostituirlo con un override per MainMethod che chiama solo Methods 1, 2 e amp; 3.

    
risposta data 21.11.2013 - 20:25
fonte
0

Sembra che quello che vuoi sia il Pattern di strategia . I tuoi metodi sono la tua "Strategia" che segue un'interfaccia. I tuoi metodi restituiranno float e prenderanno due float come argomenti per aiutare a illustrare il modello di strategia:

interface MathsStrategy {
    float execute(float a, float b);
}

e hai alcune classi che implementano questa interfaccia:

// Method1
class Add implements MathsStrategy {
    public float execute(float a, float b) {
        return a + b;  
    }
}

// Method2 
class Subtract implements MathsStrategy {
    public float execute(float a, float b) {
        return a - b;  
    }
}

// Method3
class Multiply implements MathsStrategy {
    public float execute(float a, float b) {
        return a * b;  
    }
}

e potresti racchiudere Method1 , Method2 e Method3 insieme mentre li usi in un gruppo:

// Composite of Method1, Method2 and Method3
class CompositeOf123 implements MathsStrategy {
    public float execute(float a, float b) {
        return new Add().execute(a, b) + new Subtract().execute(a, b) + new Multiply().execute(a, b);  
    }
}

Ok, inventato ma spero che tu capisca il punto.

Quindi abbiamo i metodi che variano:

// First example of Method4
class Divide implements MathsStrategy {
    public float execute(float a, float b) {
        return a / b;  
    }
}

// Second example of Method4
class Exponentiation implements MathsStrategy {
    public float execute(float a, float b) {
        return a ^ b;  
    }
}

Riassumendo, abbiamo alcuni metodi che usiamo frequentemente e che vogliamo usare in composito e alcune implementazioni differenti che vogliamo usare a volte, così creiamo strategie diverse per questi.

Ora, se li passiamo alla classe come parte del costruttore, allora non abbiamo nemmeno bisogno dell'ereditarietà. È il comportamento del metodo che deve cambiare, non la classe stessa. In questo modo non è necessario modificare la classe per implementare una strategia diversa, basta passare la strategia al costruttore della classe quando creiamo un'istanza della classe.

Quindi la tua classe diventa:

public class MyClass
{
    private MathsStrategy strategy;

    public MyClass(MathsStrategy maths) {
        strategy = maths;
    }

    public float MainMethod(float a, float b) {
        return strategy.execute(a, b);
    }
}

Ora devi solo creare istanze di MyClass che utilizzeranno le diverse strategie come richiesto:

float a = 15.0F;
float b = 25.0F;
float result;

MyClass addition = new MyClass(new Add());  // This instance does addition
result = addition.MainMethod(a, b);

MyClass composite = new MyClass(new CompositeOf123()); // This instance does addition, subtraction and multiplication
result = composite.MainMethod(a, b);

MyClass exp = new MyClass(new Exponentiation());  // This instance does one version of Method4
result = exp.MainMethod(a, b);

Queste istanze delle classi eseguiranno la strategia appropriata (in questo caso una semplice equazione matematica) quando chiamerai il metodo MainMethod . Ciò fornisce una struttura di classe molto più pulita e una separazione delle preoccupazioni.

    
risposta data 22.11.2013 - 05:33
fonte
0

Potresti anche passare delegati alla tua funzione. Il metodo1, ..., metodo4 sembra avere la stessa firma. Crea una funzione di ordine superiore (il tuo nuovo metodo principale) che accetta più delegati come parametri e li esegue. Scusa per l'esempio mancante, sono al telefono. Lo modificherò domani però

--- --- Esempio

public delegate void ADelegate();

public void someOtherFunction() {

    var methodsToCall = method1 + method2 + method3 + method4;
    newMain(methodsToCall);

    var methodsToCall -= method4;
    newMain(methodsToCall);
}

public void newMain(ADelegate aDelegate){
    aDelegate();
}    

public void method1() {...};
public void method2() {...};
public void method3() {...};
public void method4() {...};

Non è stato testato, ma penso che tu possa avere l'idea

    
risposta data 21.11.2013 - 22:34
fonte

Leggi altre domande sui tag