Come faccio a rendere questo esempio Open / Closed anche obbedire alla Single-Responsibility?

7

Questo è un semplice esempio, ma riflette una tensione tra i principi SOLIDI con cui spesso mi trovo a dover lottare.

Un esempio popolare del Principio Aperto / Chiuso (es. [1] , [2] ) immagina di avere molte classi Shape e un metodo drawShape() , ad esempio :

// Open-Close Principle - Bad example
 class GraphicEditor {

    public void drawShape(Shape s) {
        if (s.m_type==1)
            drawRectangle(s);
        else if (s.m_type==2)
            drawCircle(s);
    }
    public void drawCircle(Circle r) {....}
    public void drawRectangle(Rectangle r) {....}
 }

Usando l'Open / Closed Principle (OCP), osserviamo che questo codice dovrebbe essere modificato per ogni nuova forma che aggiungiamo; vogliamo chiudere la necessità di modifiche e consentire l'estensione come un modo migliore per aggiungere nuovi comportamenti.

Gli esempi offrono quindi quanto segue come soluzione conforme a OCP:

// Open-Close Principle - Good example
 class GraphicEditor {
    public void drawShape(Shape s) {
        s.draw();
    }
 }

 class Shape {
    abstract void draw();
 }

 class Rectangle extends Shape  {
    public void draw() {
        // draw the rectangle
    }
 } 

Come dimostrazione dell'OCP in astratto, questo è un buon esempio. Tuttavia, questa soluzione mi sembra una palese violazione del Principio di Responsabilità Unica (SRP) . Abbiamo iniziato con la "responsabilità di disegno" separata nella sua classe e abbiamo risolto il nostro problema di OCP abbattendo quella "responsabilità di disegno" con la semplice definizione di una forma.

Ad esempio, se il disegno implica l'interazione con una GUI, questo esempio accoppia la definizione di una forma semplice con le operazioni della GUI! Sembra sbagliato.

Quale sarebbe una buona riprogettazione qui che sarebbe conforme a OCP e a SRP?

    
posta Standback 20.03.2017 - 12:57
fonte

1 risposta

14

Disegna su una tela. Il disegno non dovrebbe richiedere interazione con l'interfaccia utente, ma semplicemente il trasferimento della forma disegnata nell'interfaccia utente per la visualizzazione.

interface Shape {
    void draw(Canvas canvas);
}

class GraphicEditor {
    private Canvas mainCanvas;

    void drawShape(Shape s, Point location) {
        Canvas subCanvas = mainCanvas.subCanvas(location, s.width, s.height);
        s.draw(subCanvas);
        mainCanvas.copy(subCanvas, location);
    }
}

Una risposta più generale:

Progetta i tuoi metodi per trasferire i dati, piuttosto che avere effetti collaterali. Definisci DTO (come una tela) e passali nella gerarchia.

Ogni oggetto ha collaboratori. Pensateli come servizi che l'oggetto padre utilizza per ottenere informazioni e per eseguire attività che non fanno parte della sua responsabilità principale (SRP). I DTO risultanti sono il "prodotto" di tali servizi. L'oggetto genitore può fare con questi prodotti ciò che desidera (disegnarli sullo schermo, inviarli via cavo, memorizzarli in un DB, ...)

    
risposta data 20.03.2017 - 13:04
fonte

Leggi altre domande sui tag