Violazione e soluzione per principio di responsabilità singola

3

EDIT 2017/02/13 : controlla il repo come è adesso. Il primo esempio è la soluzione con SRP in mente, ma successivamente introducendo altri principi di SOLID .. Avrei dovuto spiegarlo meglio.

Sto cercando di ottenere un buon esempio su una violazione e su come risolvere il principio SR. Voglio essere sicuro però che sia buono, e che non scrivo nulla che non abbia senso - quindi qualsiasi input è benvenuto, o come potrei spiegarlo meglio. Lo faccio per insegnare a me stesso e capire meglio la "S" di SOLID con la teoria e l'esempio.

Ecco l'esempio: link - o sotto come riferimento.

Principio di responsabilità singola

A class should only have only one reason to change.

Esempio di violazione

La classe Person è responsabile della detenzione di dati relativi alle persone, ma contiene anche una funzione Format che emette i dati di questa persona in un determinato formato. Attualmente questo metodo accetta un parametro su cui la funzione decide quale algoritmo utilizzare restituisce i dati nel formato richiesto.

L'esempio di seguito viola il principio di responsabilità singola perché la classe Person ora è responsabile della conservazione dei dati della persona e formattazione quei dati.

class Person {
    public string FirstName { get; set; }
    public string LastName  { get; set; }
    public Gender Gender { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Format(string formatType) {
        switch(formatType) {
            case "JSON":
               // implement JSON formatting here
               return jsonFormattedString;
               break;
            case "FirstAndLastName":
              // implementation of first & lastname formatting here
              return firstAndLastNameString;
              break;
            default:
              // implementation of default formatting
              return defaultFormattedString;
        }
    }
}

Come risolvere?

Possiamo risolvere questo problema assicurandoci che la classe Person sia responsabile solo della conservazione dei dati e non responsabile della formattazione di tali dati. Quindi estraiamo il metodo Format e introduciamo un'altra classe responsabile della formattazione di un libro.

class PersonFormatter {
    public string Format(Person person, string formatType) {
        switch(formatType) {
            case "JSON":
               // implement JSON formatting here
               return jsonFormattedString;
               break;
            case "FirstAndLastName":
              // implementation of first & lastname formatting here
              return firstAndLastNameString;
              break;
            default:
              // implementation of default formatting
              return defaultFormattedString;
        }
    }
}



class Person {
    public string FirstName { get; set; }
    public string LastName  { get; set; }
    public Gender Gender { get; set; }
    public DateTime DateOfBirth { get; set; }
}

Potremmo sostenere che questo esempio contiene ancora violazioni contro SOLID , e questo è vero. Poiché il metodo format mantiene ancora troppe responsabilità in quanto deve implementare troppi dettagli su come viene formattato. Lo risolveremo in seguito con uno o più degli altri principi: il principio Inversione dipendenza e il principio Segragation interfaccia . Ma almeno ora possiamo cambiare l'implementazione della formattazione di una Persona senza influenzare la classe Person stessa - come afferma lo SRP.

    
posta Yves Schelpe 11.02.2017 - 13:12
fonte

3 risposte

3

In primo luogo non usare un argomento stringa per decidere quale tipo di formato vuoi, farà sembrare il resto brutto ed è molto incline a errori che il compilatore non può catturare.

In secondo luogo non c'è nulla di sbagliato in un oggetto che tiene i dati in grado di formattare se stesso. Il resto del codice dovrebbe idealmente non preoccuparsi di quali membri ha l'oggetto. Non vuoi cambiare 100 posizioni solo perché hai aggiunto un secondo nome.

In terzo luogo, ed è qui che entra in gioco la singola responsabilità, l'istanza di una persona non dovrebbe sapere come fare a se stessa. Dovrebbe solo sapere di qualcosa che può chiamare con le informazioni in sé (nome, cognome, ecc.) Trasformando tutto in json. Potrebbe essere necessario creare una raccolta di nomi e valori di campi e inviarli a JSON Creator.

Quindi crea una classe generale che qualsiasi classe può usare quando deve json stesso che aggiunge parentesi, delimitatori ecc.

E non dovresti avere tutta la logica all'interno del metodo di formattazione generale, dividerlo in un metodo per ogni tipo e chiamarli dal metodo di formato generale.

    
risposta data 11.02.2017 - 14:21
fonte
2

Ho letto solo la parola "Persona", prima che pensassi "uh-oh". Una vasta classe di entità è destinata a violare alla fine SRP, in qualsiasi sistema non banale.

Vedo così tanti libri di testo che forniscono esempi di classi che tentano di modellare un'entità, quando IMO dovrebbero davvero modellare una gamma ristretta di comportamenti. Prendiamo ad esempio i POCO, certo, si potrebbe definire uno per "Persona", ma in realtà hanno una gamma estremamente limitata di comportamenti che ha senso per loro. Non sono definiti per essere in grado di fare tutto ciò che una persona può fare.

Questo è un esempio estremo, ma il principio è ancora mele, le tue classi dovrebbero essere definite in modo ristretto da ciò che NON SONO genericamente da ciò che SONO, è così che stai dalla parte buona dell'SRP.

    
risposta data 13.02.2017 - 15:03
fonte
1

class Person now is responsible for keeping data of of the person and formatting that data.

Il segno distintivo di OOP è l'accoppiamento dei dati con il comportamento. Se hai una classe il cui unico scopo è solo quello di mantenere gli accessor ai dati, quello che hai è un modello di dominio anemico . Una classe anemica è una struttura di dati, non un oggetto, quindi in realtà non stai scrivendo OOP quando separi i dati dal comportamento. Un buon oggetto nasconde i suoi dati e espone solo comportamenti.

Sebbene ci siano molte ragioni per cui una struttura dati più il codice procedurale possano talvolta essere una soluzione complessivamente migliore di un oggetto reale con OOP, un oggetto anemico contraddice fondamentalmente l'OOP.

In termini di SRP, PersonFormatter fa ancora troppo. PersonFormatter esegue la formattazione per JSON, per nome semplice, ecc. Quello che suggerirei è definire un'interfaccia IPersonFormatter che abbia implementazioni multiple:

interface IPersonFormatter {
    public string Format(Person person);
}

class JSONPersonFormatter : IPersonFormatter {
    ...
}

class TemplateStringPersonFormatter : IPersonFormatter {
    public TemplateStringPersonFormatter(String fmtString) {
        ...
    }
    ...
}

Nota sull'internazionalizzazione: molte persone hanno un nome che non si adatta al modello.

    
risposta data 13.02.2017 - 14:49
fonte