Il principio aperto / chiuso, come funziona per aggiungere entità?

0

Non sono nemmeno sicuro di come chiedere questo, ma qui va:

Diciamo che ho un'applicazione Web che incorpora Insegnanti, Corsi e Studenti in modo tale da poter fare cose come questa (pseudocodice):

teacherQuery = new TeacherQuery;

teacher = teacherQuery.getById(teacherId); // teacher is a Teacher object

foreach (course in teacher.getCourses()) {
    course.printName();
    foreach (student in course.getStudents()) {
        student.printName();
    }
}

studentQuery = new StudentQuery;
students = studentQuery.getAllForCourse(courseId); // students is a 
collection of Student objects

foreach (student in students) {
    foreach (teacher in student.getTeachers()) {
        // getTeachers() returns of a Collection of unique Teachers for all the
        // Courses for the Student.
        teacher.printName();
    }
}

Ci sono molti modi per gestirlo, ma la mia domanda riguarda il cambiamento.

Diciamo che una volta che l'applicazione Insegnante / Corso / Studente funziona, il il cliente dice che vuole lasciare che gli studenti formino gruppi di studio, così che ogni studente può avere più gruppi di studio, e ciascun gruppo di studio può avere più studenti, ma ciascuno StudyGroup appartiene a un solo corso.

Lo sto masticando, e mi sembra di aggiungere inevitabilmente StudyGroups (o l'equivalente) significa apportare modifiche a un intero gruppo di classi. Oltre alla creazione StudyGroup e StudyGroupQuery, dovrai aggiungere getStudyGroups () a Studente e Corso e forse anche Maestro, e dovrai aggiungere getAllForStudyGroup () su StudentQuery e forse altre cose.

E, più precisamente, ogni volta che aggiungi un nuovo tavolo, dovrai farlo aggiungi nuovi metodi a ogni classe che ha una relazione con quella tabella.

Un approccio sarebbe quello di parametrizzarlo, in modo che invece di teacher.getStudyGroups () hai:

genericQuery.get('StudyGroups').by('Teacher').with('id',teacher.getId()).through('Courses').unique()

... o qualcosa del genere, ma non vorrai copiarlo e incollarlo ogni posto dove devi elencare i gruppi di studio supervisionati da un determinato insegnante (sei tu?) questo significa che vorresti creare getStudyGroupsByTeacher () da qualche parte, il che significa che dovrai modificare quel "luogo" quando è ora di farlo aggiungi getTextbooksByTeacher ().

Sono tentato di concludere che a volte ci sono cambiamenti che dicono solo l'Open / Closed principio di andare all'inferno, e questo è tutto quello che c'è da fare. Mi manca qualcosa?

(Una coppia di note obbligatorie: il codice sopra riportato è solo un esempio e io non sono letteralmente cercare un modo per incorporare gruppi di studio in un'applicazione di corso esistente. io Sono interessato a conoscere l'architettura, ma nel contesto di "Quale architettura il migliore consente di incorporare nuovi tipi di entità / tabelle? "non" Qual è il migliore architettura per un'applicazione Web? ")

    
posta M. Christian 26.09.2017 - 22:04
fonte

4 risposte

4

Non sono i tuoi cambiamenti che hanno detto al principio aperto / chiuso di andare all'inferno. Era il design. OCP dice che è un design migliore che consente di far arrivare il cambiamento, non dal codice di riscrittura, ma dall'aggiunta di un nuovo codice.

Apart from creating StudyGroup and StudyGroupQuery, you're going to have to add getStudyGroups() to Student and Course and maybe even Teacher, and you're going to have to add getAllForStudyGroup() to StudentQuery and maybe some other stuff.

Bene, no. No, non lo fai.

Non devi aggiungere la conoscenza dei gruppi di studio a tutto ciò che riguarda i gruppi di studio. Potresti semplicemente far sapere al gruppo di studio di cosa è composto, dargli i comportamenti di cui hai bisogno e fargli capire come eseguirli. Questo è talvolta chiamato " Tell, non chiedere "

studyGroup.printStudents();
studyGroup.printTeachers();
studyGroup.printCourse();

Quindi StudyGroup ne incapsula completamente il contenuto, quindi nient'altro deve assumersi la responsabilità di farlo funzionare per esso. Metti il metodo dove sono i dati e non devi sfogliare i dati. E questo significa che non devi toccare le altre classi.

    
risposta data 26.09.2017 - 22:44
fonte
3

Penso che tu abbia frainteso lo scopo del principio aperto / chiuso (che ho intenzione di abbreviare OCP per il resto di questo). L'OCP non significa che se è necessario aggiungere un'entità che nessuna classe o altro oggetto è autorizzato a modificare. Dice che un algoritmo / funzione che funziona su più tipi di oggetti non dovrebbe essere modificato ogni volta che viene aggiunto un nuovo tipo di oggetto.

Un buon esempio di OCP sarebbe la stampa di oggetti come stringhe. Supponiamo che tu abbia un metodo chiamato PrintThing(object o) . Un modo per scriverlo sarebbe come questo

void PrintThing(object o)
{
    if (o is int)
    {
        //do some stuff here to convert an int to a string then print it
    }
    else if (o is Foo)
    {
        //Convert relevant information from a Foo object to a string representation
    }
    else if (o is ...)
    //...
}

Questo ti sfuggirà di mano molto velocemente. Ogni volta che aggiungi un nuovo tipo, devi aggiungere un altro blocco if a quel metodo. Tipi incorporati, tipi di sistemi, tipi di libreria, ecc. Questo diventerà un incubo non gestibile in fretta.

Molte lingue (C # per esempio) lo fanno in modo che ogni singolo oggetto abbia un metodo ToString . Per la maggior parte degli oggetti, utilizza solo l'impostazione predefinita che non è particolarmente interessante, sebbene possa essere sostituita quando necessario. Quindi potresti scrivere PrintThing come segue:

void PrintThing(object o)
{
    Console.Write(o.ToString());
}

Ora non importa ciò che lancio a PrintThing sarà in grado di gestirlo (poiché object definisce un metodo ToString e tutto eredita da object .

Quindi la mia funzione non ha bisogno di modifiche ogni volta che è necessario gestire un nuovo tipo. Questo è ciò che l'OCP sta facendo. Che il tuo codice / funzioni / algoritmi non debbano essere modificati cambia ogni volta che viene aggiunto un nuovo tipo. Non significa affatto che il codice non debba essere modificato per gestire nuove funzionalità.

    
risposta data 26.09.2017 - 22:46
fonte
2

I've been chewing this over, and it feels like inevitably adding StudyGroups (or the equivalent) means making changes to a whole bunch of classes. Apart from creating StudyGroup and StudyGroupQuery, you're going to have to add getStudyGroups() to Student and Course and maybe even Teacher, and you're going to have to add getAllForStudyGroup() to StudentQuery and maybe some other stuff.

Un'opzione è ... solo non farlo . Ecco come implementare StudyGroups:

  1. Crea classi StudyGroup e StudyGroupQuery
  2. Non aggiungi metodi a Student, Course, Teacher o StudentQuery.
  3. Implementa operazioni come "ottieni gruppi di studio per studenti" in qualsiasi delle tue nuove classi abbia più senso.

A che classe appartiene un metodo "ottieni gruppi di studio per studenti"? La soluzione più approfondita probabilmente dovrebbe avere una classe StudyGroupRepository che rappresenta un'origine dati che può fornire informazioni sui gruppi di studio.

Ma potrebbe essere eccessivo. Potresti già avere una classe StudentRepository che fornisce informazioni sugli studenti. Avete in programma di avere un database che abbia studenti ma non supporti gruppi di studio? In caso contrario, sarebbe opportuno andare avanti e modificare la classe StudentRepository per contenere il metodo "ottieni gruppi di studio per studenti". Il principio aperto / chiuso non dice che non si dovrebbe modificare il codice esistente per estenderlo; dice semplicemente che il codice non deve essere progettato in modo che abbia da modificare per estenderlo.

    
risposta data 26.09.2017 - 22:41
fonte
0

La cosa fondamentale da ricordare che va di pari passo con il principio di Open Closed è il principio di responsabilità singola . Ciò significa che anziché migliorare per sempre le tue classi con nuove funzionalità che richiedono che vengano modificate, puoi progettare la versione iniziale delle classi con responsabilità limitata, testare tali modifiche e sigillare quelle classi.

È quindi possibile migliorare queste classi suddividendole e aggiungendo un ulteriore livello di responsabilità senza modificare le classi di base. Le tue super classi ora sono Apri per estensione , ma sono Chiuse per modifica .

    
risposta data 27.09.2017 - 01:43
fonte