Dove si trova il Principio di sostituzione di Liskov in una sottoclasse che passa argomenti extra a richiami simili, strettamente correlati?

1

Segui i seguenti esempi, che sono stati abbreviati:

BasicButton:

public class BasicButton
{
    private var m_fOnClick:Function;

    public function BasicButton(pOnClick:Function)
    {
        m_fOnClick = pOnClick;
    }

    .
    .
    .
        // when the button has been clicked:
        m_fOnClick();
    .
    .
    .
}

DirectionalButton:

public final class DirectionalButton extends BasicButton
{
    private var m_eDirection:int;

    private var m_fOnClick:Function;

    public function DirectionalButton(pDirection:int, pOnClick:Function)
    {
        m_eDirection = pDirection;
        m_fOnClick = pOnClick;
        super(onClick);
    }

    private function onClick():void
    {
        m_fOnClick(m_eDirection);
    }
}

La lista dei parametri del callback è una violazione del Principio di sostituzione di Liskov? So che i costruttori non lo violano, a seconda del modo in cui il settore interpreta questo principio, ma se fare casino con la lista dei parametri del callback costituisce una mutazione di un output comune o qualsiasi cosa sembra essere un po 'meno chiara.

Da un lato, entrambe le classi utilizzano una funzione chiamata pOnClick che viene chiamata ogni volta che viene cliccato e il codice di creazione dell'istanza è in grado di passare in qualsiasi funzione a cui si vuole riferire pOnClick . D'altra parte, poiché i costruttori tendono ad essere astratti da LSP, questi due parametri di pOnClick nei due costruttori potrebbero essere interpretati come riferiti a cose molto diverse.

Anche se i due parametri pOnClick sono interpretati come due input separati, c'è ancora qualche ambiguità su quanto il pOnClick delle sottoclassi effettivamente sovrascriva o distrugge la superclasse (in un senso strettamente virtuale, pubblicamente esposto), e come molto invece semplicemente passa - in un senso molto virtuale - l'equivalente di null al% ancora esistente della superclassepOnClick.

È un po 'difficile trovare le parole esatte per quello che sto descrivendo, ma in generale, dove si trova LSP in questo? Questa è una violazione? In tal caso, si può rimediare semplicemente riscrivendo DirectionalButton.onClick come segue?

private function onClick():void
{
    try
    {
        m_fOnClick(m_eDirection);
    }
    catch (e:Error)
    {
        m_fOnClick(); // This is not how I would set this up in real life.
    }
}
    
posta Panzercrisis 20.01.2015 - 23:32
fonte

1 risposta

3

Il test è se puoi prendere codice scritto per interagire con la classe base e senza modifiche , ma farlo funzionare con un'istanza della classe derivata. Ciò significa che nella maggior parte dei casi, il tuo esempio violerebbe LSP.

Tuttavia, in alcune circostanze potrebbe non farlo. Se stai utilizzando un linguaggio come JavaScript che essenzialmente ignora i parametri aggiuntivi e quel parametro non è cruciale semanticamente, potresti dimostrare che non viola l'LSP.

In un linguaggio tipizzato dinamicamente, potresti dimostrare che il tuo rimedio proposto non viola l'LSP. Tuttavia, non credo che potresti ottenere qualcosa del genere per compilare la maggior parte delle lingue tipizzate staticamente.

Un rimedio che potrebbe funzionare in qualsiasi linguaggio è semplicemente cambiare la classe base per aggiungere il parametro opzionale. Però sembra piuttosto icky.

Il modo più semplice per non violare accidentalmente i principi di progettazione dell'eredità è di non usare l'ereditarietà. La classe del pulsante direzionale può contenere un pulsante di base invece di ereditarlo e inoltrare qualsiasi evento con il parametro aggiunto. Questa è una buona opzione se non devi mai inserire un pulsante direzionale in codice che è stato originariamente progettato per interagire con uno di base.

Un'altra opzione molto flessibile per passare i dati arbitrari a un callback è invece passare in una chiusura al costruttore.

    
risposta data 20.01.2015 - 23:55
fonte

Leggi altre domande sui tag