Klassenstruktur OOP

  • C#

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von Fortender.

    Klassenstruktur OOP

    Ich bin gerade dabei ein älteres Projekt nochmal völlig neu zu schreiben und diesmal an die OOP anzupassen.
    Es geht um einen Simulator der einen vereinfachten PC simuliert (insbesondere den Von-Neumann Zyklus hervorheben soll).
    Bestehend aus RAM, CPU und ALU. Jedes der Komponenten werden weitere Komponenten untergeordnet wie z.b. Register (Instruction Register) und
    Program Counter. Verbunden werden alle Komponenten durch ein vereinfachtes Bus-System (Adressbus, Datenbus usw).
    1. Wie realisiere ich das Bus-System am besten? Der Einfachheit halber verhält sich im Simulator jeder Bus, wie eine Art Register. Er nimmt also Werte auf und kann sie auch wieder abgeben.
    2. Das die Komponenten (über den Bus) miteinander "kommunizieren" können, muss ich im Konstruktor jeder Klasse ein Parent mitliefern oder eine Simulator Instanz(oder?). Was wäre stylistisch schöner? Ich hätte jetzt eine abstrakte Klasse (TComponent) angelegt, welche als Eigenschaft Parent mitliefert (vom Typ TComponent). Jede untergeordnete Klasse erbt dann davon.

    Würde dann in etwa so aussehen:

    C#-Quellcode

    1. abstract class TComponent {
    2. public TComponent Parent { get; set; }
    3. public TComponent(TComponent parentComponent) { Parent = parentComponent; }
    4. }
    5. abstract class TRegister {
    6. public int _value;
    7. public TRegister(TComponent parent) : base(parent) {
    8. _value = 0;
    9. }
    10. public int Value { get { return _value; } set { /*value im wertebereich halten*/ }
    11. public event EventHandler ValueChanged() //Alles nur skizziert
    12. }
    13. class Simulator : TComponent {
    14. public Simulator() : base (null) {
    15. Cpu = new TCpu(this);
    16. Memory = new TMemory(this);
    17. Alu = new TAlu(this);
    18. }
    19. public TCpu Cpu ...
    20. public TMemory Memory ...
    21. public TAlu Alu ...
    22. }
    23. // und so weiter..


    MfG Tim
    Hi. Andere Idee: Da du dich offensichtlich für Rechnerarchitektur interessierst, würde ich dir empfehlen, dieses Buch zu lesen: amazon.de/Computerarchitektur-…w-Tanenbaum/dp/3827371511

    Ich weiß, dass meine Antwort nichts mit der Frage zutun hat, aber es ist vermutlich besser, wenn du die Hintergründe der Architekturen verstehst anstatt irgendwas zu programmieren, was du später eh nie wieder benutzt und das Prinzip möglicherweise unvollständig abbildet. Eine CPU zu schreiben ist sehr aufwändig, was du ja schon wissen dürftest - lies lieber was darüber und spar dir die (Neu-)Implementierung.
    Gruß
    hal2000
    Ok ich glaub ich hätte wohl noch ein paar Informationen dazupacken sollen.
    Wir hatten in der Schule das Thema Aufbau und Funktionsweise eines Computers und haben zur Veranschaulichung einen Simulator (Open Source "Johnny") verwendet. Der ist natürlich vereinfacht, sodass die Schüler das auch raffen können. Das Prinzip ist aber klar geworden. Nach der Unterrichtseinheit war klar, dass man wichtige Programmierelemente aus den Hochsprachen aber dennoch mit dem Simulator nicht mehr umsetzen konnte. Es fehlten einfach die nötigen Microbefehle. (Indizierte Adressierung und Load Constant, Direktoperanden usw). Da unser Lehrer natürlich dennoch zeigen wollte wie man das umsetzen könnte (mit änderung der Architektur/hinzufügen der microbefehle) hab ich mich entschlossen den Simulator von grundauf nachzubauen und um die nötige Funktionalität zu erweitern. Hab ich auch und funktioniert bereits einwandfrei. Jetzt möchte ich aber diesen in c# neuschreiben und zwar komplett objektorientierr dabei vorgehen. Da sind mir manche Sachen (programmiertechnisch) noch nicht schlüssig.

    MfG Tim
    hmm - vlt. verstehe ich was von Klassenstrukturen und OOP, aber von Bussen, wie sie da in deim Simulator herumfahren, verstehe ich nix. Es scheint Datenbusse, Adressbusse und weitere zu geben.
    Und "Komponenten" gibt es, was immer damit gemeint sein mag. Eine Komponente bezeichnest du als "Register" - hmm ja, das ist dann auch alle Info, die du gibst.

    Daraufhin kann ich zumindest keine sinnvollen Aussagen machen, ob hier ein relationales Datenmodell angesagt wäre oder eher ein hierarchisches, ob Klassenhierarchien angesagt sind, evtl. mit abstrakten Basis-Klassen, oder lieber Interfaces
    Oder ob man nicht was mit Delegaten deichseln kann, mit Events oder generische Klassen.
    @ErfinderDesRades Du könntest dir kurz ein Schema zum Neumanmodell ansehen, dann wüsstest du worum es geht (Link).

    Eigentlich musst du die Kommunikation über die Busse Regeln, die haben jeweils eine Auflistung von allen "angeschlossenen" Komponenten. Die einzelnen Komponenten könnten sich also am Bus subscriben.

    C#-Quellcode

    1. IDataBus databus = new DataBus();
    2. databus.Subscribe(memoryComponent);
    3. databus.Subscribe(aluComponent);
    4. // etc


    Du könntest dir (unabhängig vom Neumanmodell) ein kleines Protokoll ausdenken wie man die einzelnen Komponenten vom Bus aus anspricht. Z.B könnte der AddressBus dir Addressen für jedes Subscribte Component geben, die du beim Datenbus angeben kannst.

    ErfinderDesRades schrieb:

    Daraufhin kann ich zumindest keine sinnvollen Aussagen machen, ob hier ein relationales Datenmodell angesagt wäre oder eher ein hierarchisches, ob Klassenhierarchien angesagt sind, evtl. mit abstrakten Basis-Klassen, oder lieber Interfaces
    Oder ob man nicht was mit Delegaten deichseln kann, mit Events oder generische Klassen

    Hab das vor geraumer Zeit ja in VB runtergecodet. Da hab ich mir ein Datenmodell erstellt mit 3 DataSets jeweils eins für Mikrobefehle, Mikroprogramm, RAM.


    Zur Vereinfachung sind in der gesamten Anwendung nur Integer von 00000-19999 zulässig. Die ersten beiden Ziffern stellen den Hi-Code dar (und somit die Instruction) und die letzten drei Stellen stellen den Lo-Code (Operand) dar.
    Unter dem DataSet "Cmd" stehen später alle Microcommands, unter "Asm" der Mikroprogrammspeicher und RAM, joa.

    So hatte ich das vorher aufgebaut. Jetzt möchte ich das allerdings schön und aufgeräumt haben, objektorientiert. Zur Visualisierung der Speicher werde ich die Objekte dann an DataGridViews binden.

    ThuCommix schrieb:

    Eigentlich musst du die Kommunikation über die Busse Regeln

    Ich weiß. Allerdings ist es so, dass die Instructions nur über die Busse geschoben werden zur Visualisierung. Deshalb sagte ich ja, dass es reicht einen Bus wie einen Register zu behandeln. Dieser bekommt dann halt eben auch Befehle wie DataBus->Accumulator zugewiesen.
    Das was du mir per PN geschickt hast hab ich mir angesehen. Ich weiß nur nicht ob ich das so aufziehen soll. Da du schon das Grundgerüst schon so aufgestellt hast, hätte ich jetzt sowieso ein schlechtes gewissen das so zu machen :D

    Additionals:
    Zur Benamung der DataSets bitte mal nichts sagen, ich frage mich nämlich selbst was ich da gemacht habe.

    MfG Tim

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Fortender“ ()

    abgebildet ist da ein Dataset, keine drei. Und das hat 3 Tabellen.
    Hat das funktioniert? Dann wäre im Wesentlichen alles gelöst. Kann dich übrigens beruhigen: So ein Dataset ist durchaus OOP.
    Was aber gefällt dir daran nun nicht mehr? Dass es Dataset-generierte-Klassen sind, und du möchtest lieber selbstgebastelte?
    Das liesse sich leicht bewerkstelligen, die 3 abgebildeten Entitäten in 3 normale Klassen zu übersetzen. Und könnteste auch dran anbinden.
    Ups. Bisschen neben der Spur heute.. Ich meinen natürlich DataTables :/
    Es hat funktioniert. Allerdings war der ganze Rest nicht OOP. Und was mich hier halt eben fuchst ist, dass ich ne extra Funktion gebraucht hat, die mir zur jeweiligen ID des Microcommands, die zugehörige Funktion ausführt.
    Das hatte ich mit nem Switch-case geregelt. So würde ich mir ne Klasse schreiben wie:

    C#-Quellcode

    1. class Microcommand {
    2. public string Caption { get; private set; }
    3. public Action Command { get; private set; }
    4. public Microcommand(string caption, Action cmd) {
    5. Caption = caption;
    6. Command = cmd;
    7. }
    8. public void Execute() { Command.Invoke() }
    9. }

    Mein Mikroprogrammspeicher besteht dann nur aus Microcommand-Objekten. Da jedes Element aber auch mit einer Id/Adresse versehen werden soll (zur Veranschaulichung im Simulator -> wie im RAM numeriert), würde sich dann hier doch wieder so ein Modell mit nem typisierten DataSet anbieten. Allerdings müsste ich um den Microcommand executen zu können, dann wohl eine Column im Microcommand-Table vom Typ Object mit einbauen, welche dann mit ner Action befüllt wird. So könnte ich es mir vorstellen.. Allerdings ist ja genau das mein Problem. Mir fallen so viele Möglichkeiten ein wie ich es lösen könnte, aber ich hab immer das Gefühl ich würde stylistisch einen Fehler machen.

    MfG Tim
    ich verstehe nicht.
    Also deine MicroCommand-Klasse sieht doch gut aus. Und da kannste auch DatagridViews dran anbinden - Databinding ist nicht zwingend auf Datasetse angewiesen.
    Hier erwähne ich neben anderem auch diesen Punkt: Singleton, Serialisierung (Json, Xml) und Databinding ohne Dataset

    nur dein MicroCommand hat nu keine Asms und RamEntries, von daher sehe ich kaum einen Bezug zu obigem Dataset-Datenmodell.

    Aber auch wenn du bei Dataset bleiben willst, gäbs kein Problem: Man kann mittels partialer Klasse der CmdRow ganz problemlos eine Action-Property andrehen. Die wird dann zwar nicht per Databinding angezeigt, aber ich wüsste eh nicht, wie man einen Delegaten auf einem Control anzeigen wollen würde (bei deiner MicroCommand-Klasse wird da wohl einfach der Datentyp jeweils stehen).

    ErfinderDesRades schrieb:

    Also deine MicroCommand-Klasse sieht doch gut aus. Und da kannste auch DatagridViews dran anbinden

    Ja das weiß ich, dass ich die auch so anbinden kann. Hätte ich dann auch so vorgehabt.

    ErfinderDesRades schrieb:

    Hier erwähne ich neben anderem auch diesen Punkt: Singleton, Serialisierung (Json, Xml) und Databinding ohne Dataset

    Werde ich mir mal durchlesen :)

    ErfinderDesRades schrieb:

    nur dein MicroCommand hat nu keine Asms und RamEntries, von daher sehe ich kaum einen Bezug zu obigem Dataset-Datenmodell.

    Ja Mikroprogrammspeicher und Ram baut ja darauf auf. Allerdings hatte ich mir hier was überlegt. Für meinen Mikroprogrammspeicher würde ich eine extra Klasse basteln die von IEnumerable erbt. Da könnte ich dann nützliche Events und Methoden mit einbauen. Ich möchte den Mikroprogrammspeicher und den Ram speichern und laden können. Das würde ich dann auch direkt dadrin lösen.
    Für meine Microcommands hätte ich dann ne kleine Wrapperklasse die ein Mikroprogrammitem dann darstellt. Mein Microcommand hat ja eine Caption wie z.b. "ram->db" und die einzelnen Makros haben ja Namen wie "ADD", "SUB" usw. Die stehen dann im ersten Item eines jeden Makros.
    Ich pack den "schon fertigen" Simulator mal in den Anhang und dann könnt ihr euch selbst mal ein Bild davon machen.
    Klickscht hier

    MfG Tim
    jo, ist denkbar.
    Musst halt überlegen, ob das die angemessene Modellierung ist - die Frage würde lauten:
    Ist ein Microprogramming eine Auflistung von MemoryItem, oder hat ein Microprogramming eine Auflistung von MemoryItem?
    Ich versteh ja nix davon, würde aber naiv zu hat tendieren.
    In dem Falle würde Microprogramming nicht IEnumerable<MemoryItem> implementieren, sondern es würde einfach eine Property bereitstellen, über die man die MemoryItems abrufen kann.
    Wäre auch besser geeignet für Databinding, zB als Darstellung in einem ParentChild-View.
    Und siehe auch Stichwort "Composition over Inheritance".

    ErfinderDesRades schrieb:

    Ist ein Microprogramming eine Auflistung von MemoryItem, oder hat ein Microprogramming eine Auflistung von MemoryItem?

    Der Mikrobefehlsspeicher ist im Prinzip nur eine Auflistung, allerdings eine Auflistung vordefinierter Länge. Die Werte sind ja auf 19999 begrenzt. Jede Instruction wird im Instruction Register aufgetrennt in Hi bzw. Opcode und Locode bzw. Operand. Der Opcode steht für die Opcode*10 'te Speicheradresse im Mikrobefehlsspeicher. Man könnte auch sagen, jeder Opcode definiert so also den Beginn deines Makros im Mikrobefehlsspeichers. Um das Modell vielleicht besser zu verstehen, könntest du dir den Simulator den ich in einem der vorherigen Posts angehängt habe herunterladen und mal spaßeshalber testen. Dann müsste klar werden was ich erreichen möchte.

    Also um auf deine Frage zu antworten: Der MicroProgramMemory ist eine Auflistung von MicroProgramMemoryItems sogesehen.
    Klar könnte ich auch einfach eine Liste nehmen und die mit externen Funktionen verwalten, allerdings wäre es doch mit ner Klasse die IEnumerable<MicroProgramMemoryItem> implementiert eleganter und erfordert keine externen Funktionen. Zusätzlich könnte ich dann noch sinnvolle Events mit einbauen wie z.b. das Ändern der Liste.

    MfG Tim
    ich muss gestehen, wirklich interessieren tu ich mich nicht für derlei low-level-rechner-Architektur-Theorie.
    Normalerweise starte ich auch keine downgeloadeten Exen, aber diesmal doch, aber hat mir nix gebracht.
    Hab eine Tabelle geseehen, viele Controls, Buttons, Textboxen, grüne und rote Pfeile, und manches kommt und manches verschweindet, wenn man iwas klickst, das meiste bewirkt aber nix erkennbares.
    Und beim zumachen werd ich gefragt, ob ich abspeichern will, was ich dankend ablehne.

    egal

    Fortender schrieb:

    Mikrobefehlsspeicher ist im Prinzip nur eine Auflistung, allerdings eine Auflistung vordefinierter Länge.
    das käme dann vlt. einem Array nahe.

    Ansonsten scheinst du ja schon einen Plan zu verfolgen, groß Verbesserungen kann ich dir nicht vorschlagen, dazu versteh ich zuwenig vonne Materie.
    Du hättest mal ein Programm zum Spaß schreiben können.
    Da der Program Counter per default auf 0 steht fängt das Programm auch bei RAM-Adresse 000 an. Da auf ne Zelle klicken und dann kannste nen Wert festlegen.
    Mal ein kleines Speicherdiagramm in dem ein Wert mit dem anderen addiert wird und in einer weiteren Speicherzelle abgespeichert wird:

    Quellcode

    1. MEM[000] = 01 999 (TAKE 999)
    2. MEM[001] = 02 998 (ADD 998)
    3. MEM[002] = 04 997 (SAVE 997)

    Mit diesem Programm würde man Speicherzelle 999 in den Akkumulator laden, Speicherzelle 998 daraufaddieren und in Speicherzelle 997 speichern.
    Beim Ausführen des Programms unterscheide ich zwischen MicroSteps und MacroSteps. Ein Microstep würde bedeuten, dass der nächste Microcommand (z.b. ram->db) ausgeführt wird. Ein Macrostep würde bedeuten, dass ein Macro ausgeführt wird. (klingt logisch)
    Ein Macro besteht ja aus einem (wohl eher selten^^) bzw. mehreren Microcommands die im MicroProgramMemory liegen.
    Nicht immer gleich alles ablehnen was man vor die Füße geworfen bekommt. Der Simulator hat schon seinen Sinn.

    MfG Tim
    hast du oben buttons mit tooltips drauf. Die Tooltips sind eigentlich selbsterklärend. Der einzelne Pfeil sollte dich die Macros durchsteppen lassen. Also ein Klick = ein Makro. Der doppelpfeil lässt das Teil abspielen bis zum nächsten HLT. Also in der Regel das komplette Programm.
    HLT solltest du am Ende deines Programms einfügen, da ansonsten der fetch-cycle gelooped wird sofern keine anderen Makros mehr an der aktuellen Speicherstelle stehen.

    MfG Tim