Gestione di più raccolte in C #

3

Attualmente sto cercando di imparare C # e voglio migliorare la mia comprensione di Object Oriented Programming (OOP). Spero di ottenere questo risultato sperimentando un piccolo programma che tiene traccia dei miei compiti / informazioni sulla scuola. Ho costruito lezioni relative alle mie Istituzioni, Semestri (Termini), Corsi e Incarichi. La mia domanda è se ho creato e implementato correttamente le mie classi riguardo alle informazioni che rappresentano. Il mio pensiero suggerisce che le mie classi non dovrebbero ereditare l'una dall'altra in un genitore / figlio come la moda perché sono fisicamente non correlate. Tuttavia, l'accesso agli oggetti tramite elenchi a più livelli sembra poco pratico (credo). Esiste un modo migliore per farlo che non mi costringe ad accedere all'oggetto eseguendo iterazioni attraverso le raccolte, ma implementa ancora le best practice OOP?

Codice sorgente programma

Punto di ingresso / Principale

static void Main(string[] args)
{
    List<Institution> Institutions = new List<Institution>();
    Institutions.Add(new Institution("My college"));
    Institutions[0].AddNewTerm("2016", "Spring");
    Institutions[0].Terms[0].AddNewCourse("Math 210");
    Institutions[0].Terms[0].Courses[0].AddNewAssignment("Chapter 1");
    MessageBox.Show(Institutions[0].Terms[0].Courses[0].Assignments[0].Name);
}

Elenco classi

Ente

class Institution
{
    public string Name { get; set; }
    public List<Term> Terms { get; set; }

    public Institution(string UP_Name)
    {
        this.Name = UP_Name;
        this.Terms = new List<Term>();
    }

    public void AddNewTerm(string NewTermYear, string NewTermSeason)
    {
        Terms.Add(new Term(NewTermYear, NewTermSeason));
    }
}

Term

class Term
{
    public string Name { get; set; }
    public string Year { get; set; }
    public string Season { get; set; }
    public List<Course> Courses { get; set; }

    public Term(string NewSeason, string NewYear)
    {
        this.Season = NewSeason;
        this.Year = NewYear;
        this.Courses = new List<Course>();
        this.Name = (this.Season + " " + this.Year);

    }

    public void AddNewCourse(string NewCourseName)
    {
        this.Courses.Add(new Course(NewCourseName));
    }
}

Corso

class Course
{
    public string Name { get; set; }

    public List<Assignment> Assignments { get; set; }

    public Course(string UP_Name)
    {
        this.Name = UP_Name;
        this.Assignments = new List<Assignment>();
    }

    public void AddNewAssignment(string NewAssignmentName)
    {
        Assignments.Add(new Assignment(NewAssignmentName));
    }

}

Assegnazione

class Assignment
{
    public string Name { get; set; }

    public Assignment(string UP_Name)
    {
        this.Name = UP_Name;
    }
}
    
posta kylepdavis 21.01.2016 - 19:29
fonte

4 risposte

3

Non è male per un principiante. Hai ragione a essere preoccupato per l'accesso agli oggetti tramite elenchi a più livelli. Inoltre, l'hard coding degli indici ti farà male alla fine (ma per ora è OK).

Potresti provare a cambiare AddNewxxx() per restituire ciò che è stato aggiunto. Ad esempio, se si modifica AddNewClass di Term a

public Course AddNewCourse(string NewCourseName)
{
    Course result = new Course(NewCourseName);
    this.Courses.Add(result);
    return result;
}

nella tua funzione Main() sarai in grado di fare qualcosa di simile

var mathCourse = Institutions[0].Terms[0].AddNewCourse("Math 210");
mathCourse.AddNewAssignment("Chapter 1");
mathCourse.AddNewAssignment("Chapter 2");

e così via.

Sii paziente; c'è molto da imparare.

    
risposta data 21.01.2016 - 19:48
fonte
2

Ho qualche ulteriore intuizione che posso fornire. Innanzitutto, riguardo alle tue classi, dal momento che il nome di un'istituzione dovrebbe probabilmente rimandare lo stesso e non cambiare, una volta che l'istanziazione è l'unico momento in cui si dovrebbe consentire all'implementazione esterna di modificare quella stringa per il Nome, renderla immutabile dal punto di vista della classe. Questo sarebbe un vero incapsulamento; un getter che è pubblicamente accessibile e qualsiasi funzionalità di setter come privato. Lo stesso principio si applica ad altri campi all'interno di altre classi. Inoltre, sovrascrivere il metodo ToString () per gli oggetti definiti dall'utente è solitamente sempre abbastanza utile nei casi in cui l'oggetto ha una rappresentazione di stringa.

Tieni presente che puoi anche creare i tuoi tipi di raccolta implementando una classe che implementa IList o ICollection, ad esempio, tra le altre interfacce. Se si desidera che il proprio oggetto sia enumerabile tramite un ciclo foreach, considerare anche IEnumerable. A meno che tu non abbia delle linee guida specifiche su come dovrebbe essere utilizzata la tua classe, l'idea è generalmente quella di mantenere l'implementazione il più generale possibile per ragioni di scalabilità.

Per quanto riguarda le convenzioni di denominazione, esistono convenzioni di denominazione .NET che potresti voler seguire e prendere parte alle tue abitudini da MSDN ( collegamento ). In genere i locali usano CamelCase piuttosto che PascalCase.

Se la tua intenzione è quella di orientare l'implementazione verso le stringhe, allora le proprietà indicizzate come menzionato prima potrebbero essere il caso migliore per questo. Come molti esempi mostrano, il paradigma sembra molto simile a qualsiasi dei metodi TryParse () forniti da .NET per evitare che venga lanciata un'eccezione, ma consente di indicare se l'azione ha avuto successo o meno.

Non vedo una ragione per l'aggregazione qui perché l'erosione per me sembra che dovrebbe essere la seguente: Istituzione - > Semestre - > Termini - > Corsi - > Assegnazioni. A mio avviso, nessuno degli oggetti di livello inferiore dovrebbe esistere senza un genitore. Un semestre dalla mia esperienza passata era l'equivalente di 2 termini. Quello che probabilmente vuoi in questo caso in termini di relazione tra oggetti è Composizione.

    
risposta data 23.01.2016 - 20:50
fonte
1

Hai ragione a non usare l'ereditarietà, e in effetti penso che il tuo progetto vada bene. Diamo un'occhiata a uno dei tuoi usi del tuo codice.

MessageBox.Show(Institutions[0].Terms[0].Courses[0].Assignments[0].Name);

Mentre [0] usa qui sono standard per il test, nella maggior parte degli usi di un progetto basato su classi, non si accederà agli elenchi direttamente da qualche indice, ad esempio scrivendo il numero 0 nel codice. Invece, avresti molti loop "for-each" che svolgono alcune importanti funzioni su tutti Termini, e poi su tutti Corsi in quel termine. Queste operazioni appariranno più pulite anche quando il ciclo è separato in più blocchi con tabulazione {} , invece che tutti in un'unica riga.

La mappatura degli oggetti da uno a molti è un concetto molto importante e comune nella programmazione in generale. Il tuo sistema operativo ha molti utenti (di solito solo uno). Ogni utente può eseguire più programmi contemporaneamente. Questi programmi possono eseguire più thread ... ecc.

Non ti allontanerai mai dalla necessità di utilizzare le raccolte, sebbene la programmazione si sia evoluta per renderle più facili da usare. C # in particolare ha un meccanismo chiamato "LINQ" che ti permette di creare rapidamente "collezioni di finzioni" che rappresentano tutte le parti di un'altra collezione, ma in qualche modo cambiate. Vale la pena leggerlo da solo, però.

Risposta al commento ...

Questo è già il modo in cui funzionano tutti gli oggetti (in C #); solo per referenze. Diamo un'occhiata a questo esempio.

Course course101 = Institutions[0].Terms[0].Courses[5];
Console.Writeline(Institutions[0].Terms[0].Courses[5].Name); // "C# 101"
course101.Name = "Boring Class";
Console.Writeline(Institutions[0].Terms[0].Courses[5].Name); // "Boring Class"

L'unica volta in cui hai effettivamente un oggetto nuovo, separato è quando chiami new Course() . Altrimenti, le sole cose che stai passando in giro come variabili sono riferimenti. Quindi, nel caso tu fossi preoccupato di dover passare "institutionIndex, termIndex, courseIndex" per ogni funzione, sicuramente non ti preoccupare.

    
risposta data 21.01.2016 - 19:54
fonte
1

Una cosa che vorrei sottolineare è che tutti i tuoi metodi Add non sono necessari. Se esporti il List<T> come fai tu, puoi usarli per aggiungere. Ed è possibile utilizzare la sintassi di inizializzazione della raccolta per questo:

var institutions = new List<Institution>
{
    new Institution("My college")
    {
        Terms =
        {
            new Term("2016", "Spring")
            {
                Courses =
                {
                    new Course("Math 210")
                    {
                        Assignments =
                        {
                            new Assignment("Chapter 1")
                        }
                    }
                }
            }
        }
    }
};

A prima vista, questo non sembra migliore del tuo originale, ma credo che rifletta meglio l'intenzione del codice. (E probabilmente non sembrerà così male nel codice più realistico.)

    
risposta data 21.01.2016 - 22:25
fonte

Leggi altre domande sui tag