Frage zu Programmplanung (KlassenDesigen)

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von Duster.

    Frage zu Programmplanung (KlassenDesigen)

    Hallo Leute,

    ich habe vor kurzem angefangen eine Tutorialserie die ich in VB angefangen habe nach C# zu portieren und eigentlich neu zu schreiben
    da in der BV-Version einiges schief gelaufen ist. Die Tutorials dienen auch als Lernprogramme für mich.

    Ich habe auf @'Erfinder des Rades' gehört und einen damals von mir entwickelten Datentyp (PenData) rausgeworfen und verwende jetzt nur noch Klassen und keine Strukturen etc. mehr!. Ausserdem plane ich das ganze jetzt mehr d.h. nicht nur Oberfläche etc.!.

    Meine Frage ist, sehe ich folgendes richtig ?( :

    Ob ich die Datenübergabe in die Klassen über einen Konstruktor oder über Eigenschaften regle ist vom Tippaufwand her egal ich muss immer
    entweder den Konstruktor oder die Eigenschaften in vollem Umfang angeben um ein geändertes Objekt (Farbe, DashStyle etc.) zu erhalten.

    Aber mit den Eigenschaften bin ich näher an der Kapselung da die Background Variablen der Eigenschaften private sind!

    Die „CreatePen()“ befindet sich bei Line01 noch im Konstruktor, da es aber auch eine CrateBrush() geben wird habe ich sie aus den
    Konstruktor geholt. Über die Eigenschaften kann ich später testen ob ein Pen oder ein Brush gewünscht wird und diesen dann erstellen.

    Ich habe ein kleines Testprogramm geschrieben (und angehängt) in dem ich beide Möglichkeiten gegenüberstelle.
    Ich würde die Erstellung der Objekte mittels der Eigenschaften vorziehen. Was meint Ihr dazu?

    Vielen Dank für Eure Mühe vorab :)

    Hier etwas Code:

    Shape01:

    C#-Quellcode

    1. namespace ColorDialog_Problem
    2. {
    3. public abstract class Shape01
    4. {
    5. public Point Line01StartLocation;
    6. public Point Line01EndLocation;
    7. public abstract string Name { get; set; }
    8. public int Alpha { get; set; }
    9. public int Width { get; set; }
    10. public DashStyle DStyle { get; set; }
    11. public Size Size { get; set; }
    12. public Pen Pen;
    13. public abstract void Draw(Graphics e);
    14. }
    15. }



    Line01:

    C#-Quellcode

    1. namespace ColorDialog_Problem
    2. {
    3. class Line01 : Shape01
    4. {
    5. public override string Name
    6. {
    7. get => "Line01";
    8. set { }
    9. }
    10. public Line01() { }
    11. public Line01(int alpha, int red, int green, int blue, int width, DashStyle dStyle)
    12. {
    13. Pen = new Pen(Color.FromArgb(alpha, red, green, blue), width)
    14. {
    15. DashStyle = dStyle
    16. };
    17. }
    18. public override void Draw(Graphics e)
    19. {
    20. e.DrawLine(Pen, Line01StartLocation, Line01EndLocation);
    21. }
    22. }
    23. }



    Shape02:

    C#-Quellcode

    1. namespace ColorDialog_Problem
    2. {
    3. public abstract class Shape02
    4. {
    5. public Point Line02StartLocation;
    6. public Point Line02EndLocation;
    7. public abstract string Name { get; set; }
    8. public abstract int Alpha { get; set; }
    9. public abstract int Red { get; set; }
    10. public abstract int Green { get; set; }
    11. public abstract int Blue { get; set; }
    12. public abstract int Width { get; set; }
    13. public abstract DashStyle DStyle { get; set; }
    14. public Pen Pen;
    15. //public Brush Brush;
    16. public abstract void Draw(Graphics e);
    17. }
    18. }


    Line02:

    C#-Quellcode

    1. namespace ColorDialog_Problem
    2. {
    3. class Line02: Shape02
    4. {
    5. private int _alpha;
    6. private int _red;
    7. private int _green;
    8. private int _blue;
    9. private int _width;
    10. private DashStyle _dStyle;
    11. public override string Name
    12. {
    13. get => "Line02";
    14. set { }
    15. }
    16. public override int Alpha
    17. {
    18. get => _alpha;
    19. set => _alpha = value;
    20. }
    21. public override int Red
    22. {
    23. get => _red;
    24. set => _red = value;
    25. }
    26. public override int Green
    27. {
    28. get => _green;
    29. set => _green = value;
    30. }
    31. public override int Blue
    32. {
    33. get => _blue;
    34. set => _blue = value;
    35. }
    36. public override int Width
    37. {
    38. get => _width;
    39. set => _width = value;
    40. }
    41. public override DashStyle DStyle
    42. {
    43. get => _dStyle;
    44. set => _dStyle = value;
    45. }
    46. public Line02() { }
    47. private Pen CreatePen() // Wird noch eine CreateBrush() geben
    48. {
    49. DashStyle dStyle = DashStyle.Dash;
    50. Pen pen = new Pen(Color.FromArgb(Alpha, Red, Green, Blue), Width);
    51. pen.DashStyle = DStyle;
    52. Pen = pen;
    53. return pen;
    54. }
    55. public override void Draw(Graphics e)
    56. {
    57. CreatePen();
    58. e.DrawLine(Pen, Line02StartLocation, Line02EndLocation);
    59. }
    60. }
    61. }


    Die Verwendung:

    C#-Quellcode

    1. namespace ColorDialog_Problem
    2. {
    3. public partial class Form1 : Form
    4. {
    5. private bool _drawState = true;
    6. private string _shapeName01 = "Line01";
    7. private string _shapeName02 = "Line02";
    8. private Line01 _line01 = new();
    9. private Line02 _line02 = new();
    10. private int _line01Alpha = 255;
    11. private int _line01Red = 255;
    12. private int _line01Green = 201;
    13. private int _line01Blue = 14;
    14. private int _line01Width = 10;
    15. private int _line02Alpha = 255;
    16. private int _line02Red = 0;
    17. private int _line02Green = 0;
    18. private int _line02Blue = 255;
    19. private int _line02Width = 10;
    20. private Point _line01StartCoords;
    21. private Point _line01EndCoords;
    22. private Point _line02StartCoords;
    23. private Point _line02EndCoords;
    24. public Form1()
    25. {
    26. InitializeComponent();
    27. }
    28. private void Form1_Load(object sender, EventArgs e)
    29. {
    30. InitializeShapes();
    31. }
    32. protected override void OnPaint(PaintEventArgs e)
    33. {
    34. base.OnPaint(e);
    35. _drawState = true;
    36. _line01.Draw(e.Graphics);
    37. _line02.Draw(e.Graphics);
    38. }
    39. private void CmdColorLines_Click(object sender, EventArgs e)
    40. {
    41. if (DlgColor.ShowDialog() == DialogResult.OK)
    42. {
    43. _line01Red = DlgColor.Color.R;
    44. _line01Green = DlgColor.Color.G;
    45. _line01Blue = DlgColor.Color.B;
    46. _line02Red = DlgColor.Color.R;
    47. _line02Green = DlgColor.Color.G;
    48. _line02Blue = DlgColor.Color.B;
    49. _line01 = new Line01(_line01Alpha, _line01Red, _line01Green, _line01Blue, _line01Width, DashStyle.Dash)
    50. { Line01StartLocation = _line01StartCoords, Line01EndLocation = _line01EndCoords };
    51. _line02 = new Line02() { Line02StartLocation = _line02StartCoords, Line02EndLocation = _line02EndCoords };
    52. _line02.Alpha = Convert.ToInt32(NudAlpha.Value);
    53. _line02.Red = _line02Red;
    54. _line02.Green = _line02Green;
    55. _line02.Blue = _line02Blue;
    56. _line02.Width = _line02Width;
    57. _line02.DStyle = DashStyle.Dash;
    58. }
    59. Invalidate();
    60. }
    61. private void NudAlpha_ValueChanged(object sender, EventArgs e)
    62. {
    63. _line01 = new Line01(Convert.ToInt32(NudAlpha.Value), _line01Red, _line01Green, _line01Blue, _line01Width, DashStyle.Dash)
    64. { Line01StartLocation = _line01StartCoords, Line01EndLocation = _line01EndCoords };
    65. _line02 = new Line02() { Line02StartLocation = _line02StartCoords, Line02EndLocation = _line02EndCoords };
    66. _line02.Alpha = Convert.ToInt32(NudAlpha.Value);
    67. _line02.Red = _line02Red;
    68. _line02.Green = _line02Green;
    69. _line02.Blue = _line02Blue;
    70. _line02.Width = _line02Width;
    71. _line02.DStyle = DashStyle.Dash;
    72. Invalidate();
    73. }
    74. public void InitializeShapes()
    75. {
    76. _line01StartCoords = new Point(250, 150);
    77. _line01EndCoords = new Point(560, 150);
    78. _line02StartCoords = new Point(250, 200);
    79. _line02EndCoords = new Point(560, 200);
    80. _line01 = new Line01(_line01Alpha, _line01Red, _line01Green, _line01Blue, _line01Width, DashStyle.Dash)
    81. { Line01StartLocation = _line01StartCoords, Line01EndLocation = _line01EndCoords };
    82. _line02 = new Line02()
    83. { Line02StartLocation = _line02StartCoords, Line02EndLocation = _line02EndCoords };
    84. _line02.Alpha = _line02Alpha;
    85. _line02.Red = 0;
    86. _line02.Green = 0;
    87. _line02.Blue = 255;
    88. _line02.Width = _line02Width;
    89. _line02.DStyle = DashStyle.Dash;
    90. Invalidate();
    91. }
    92. }
    93. }


    Vielen Dank nochmal und viele Grüße bei der Hitze,
    Duster

    P.S.: Hab ich was falsch gepostet oder sind alle am Badesee :)
    Dateien

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Duster“ ()

    Duster schrieb:

    Ob ich die Datenübergabe in die Klassen über einen Konstruktor oder über Eigenschaften regle ist vom Tippaufwand her egal ich muss immer entweder den Konstruktor oder die Eigenschaften in vollem Umfang angeben um ein geändertes Objekt (Farbe, DashStyle etc.) zu erhalten.
    Aber mit den Eigenschaften bin ich näher an der Kapselung da die Background Variablen der Eigenschaften private sind!
    Das kommt drauf an. Grundsätzlich ist es so, dass Du bei einem parametrisierten Konstruktor die Werte initial festlegst. Und somit dem Nutzer sagst: Ohne die Initialwerte ergibt die Nutzung der Klasse keinen Sinn. Ich z.B. nutze solch einen parametrisierten Konstruktor bei einem speziellen TCP/IP-Server, dem gesagt werden muss, auf welchem Port er lauschen soll. Ohne Portangabe ergäbe die Nutzung bei meiner speziellen Serverklasse keinen Sinn. Gleichzeitig könnte solch ein Konstruktor auch aussagen: Die verwendeten Parameter sind nach Instanziierung so wie angegeben und bleiben auch so. Wenn Du hingegen Properties nimmst, machst Du dem Klassenuser klar: Ändere die Einstellungen, wann immer Du willst, ich sorge danach dafür, dass das alles so seine Richtigkeit hat.

    ##########

    C#-Quellcode

    1. public override void Draw(Graphics e)
    2. {
    3. CreatePen();
    4. e.DrawLine(Pen, Line02StartLocation, Line02EndLocation);
    5. }

    Wenn das Ding gemalt wird, wird erstmal ein Pen kreiert? Klingt nach Overkill. Ggf. vorher prüfen, ob Pen schon etwas ist? Wo werden all die Disposable-Objects (ok, hab bisher nur Pen gesehen) disposed?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

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

    @VaporiZed
    Tut mir leid dass ich mich so spät melde!!!!!!!

    Danke für die schnelle Antwort Vaporized.

    Bevor wir weiterreden lass mich kurz beschreiben um welche Art Programm es sich
    bei meinem Projekt handelt.: Das ganze wird ein GDI+ Tutorium bei dem der Nutzer über verschiedene Controls (List-/ Comboboxen, PropertyGrids etc.) die verschiedenen GDI+ Primitive (Line, Ellipse, Rect, Rects etc.) zeichnen lassen kann. Der Nutzer kann entscheiden ob ein Pen oder Brush genutzt wird und kan Start- und EndCaps etc. hinzu fügen. Es kann über Controls alles geändert werden was die Primitive betrifft!


    Das kommt drauf an. Grundsätzlich ist es so, dass Du bei einem parametrisierten Konstruktor die Werte initial festlegst. Und somit dem Nutzer sagst: Ohne die Initialwerte ergibt die Nutzung der Klasse keinen Sinn. Ich z.B. nutze solch einen parametrisierten Konstruktor bei einem speziellen TCP/IP-Server, dem gesagt werden muss, auf welchem Port er lauschen soll. Ohne Portangabe ergäbe die Nutzung bei meiner
    speziellen Serverklasse keinen Sinn. Gleichzeitig könnte solch ein Konstruktor auch aussagen: Die verwendeten Parameter sind nach Instanziierung so wie angegeben und bleiben auch so.

    Das ist klar. Da aber bei meinem Programm eben alles was die Primitive vom Nutzer über Contros änderbar ist, d.h. die verwendeten Parameter nach Instantiierung eben nicht bleiben wie sie sind halte ich die 2.
    Variante mit Properties für besser.

    Wenn Du hingegen Properties nimmst, machst Du dem Klassenuser klar: Ändere die Einstellungen, wann immer Du willst, ich sorge danach dafür, dass das alles so seine Richtigkeit hat.

    D.h. nicht den parameterisierten Konstruktor sondern die Properties. Habe ich Dich da richtig verstanden ?( ????

    Wenn das Ding gemalt wird, wird erstmal ein Pen kreiert? Klingt nach Overkill. Ggf. vorher prüfen, ob Pen schon etwas ist?

    Wieso Overkill, vor jedem Primitiv muss ein zugehöriger Pen erstellt werden mit dem die Form gemalt wird!
    Selbstverständlich werde ich noch prüfen ob pro Primitiv ein Pen bzw. Brush schon erstellt wurde (null) oder ob noch einer gebraucht wird. Werd noch sehen wie ich das mache vielleicht innerhalb irgendeiner Eigenschaft.

    Wo werden all die Disposable-Objects (ok, hab bisher nur Pen gesehen) disposed?

    Ja muss ich auch noch machen. Werd sehen wo es Sinn macht!.

    Viele Grüße,
    Duster
    Welche Frage?
    In Post#4 sehe ich auf die Schnelle keine Frage.

    Doch, eine, aber die ist an Vpz gerichtet:

    Duster schrieb:

    D.h. nicht den parameterisierten Konstruktor sondern die Properties. Habe ich Dich da richtig verstanden ????
    Ich könnte da allenfalls drauf antworten, dass so eine verallgemeinerte Aussage: "nicht den parameterisierten Konstruktor sondern die Properties."
    natürlich Unfug ist.
    Und das hat Vpzs sicher sicher auch nicht gemeint, wenn er sagt:

    VaporiZed schrieb:

    Das kommt drauf an...






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

    @Erfinder des Rades
    Aso, dann hab ich missverständlich geschrieben.
    Eigentlich wollte ich wissen was die bessere (cleanere) Variante ist bei meinem Projekt (Tutorial wie in Post #4 beschrieben), parameterisierter Konstruktor oder Eigenschaften?
    Nach weiterem Überlegen denke ich dass ich den Pen oder den Brush an die Draw-Funktion von aussen übergebe und in der Draw einfach teste ob Pen oder Brush Null sind.
    Ich glaube mich zu erinnern dass Du mir bei der VB-Version (letztes Jahr) sowas auch geraten hast ==> Proframmiere so einfach wie möglich und wirf PenData raus ;)

    Ich hoffe das war unmissverstänlicher?

    Grüße,
    Duster
    Jo, imo besser verständlich, aber ich habe dein Projekt nicht so im Detail vor Augen.
    Jdfs. Pen und Brush in eine Draw-Methode reinreichen klingt für mich sinnvoll.

    Nicht sinnvoll scheint mir, da immer abzuprüfen, ob sie Null sind.
    Wenn eine Methode falsch aufgerufen wird, dann darf das Proggi gerne abstürzen.
    Das ist ein sehr effizientes Mittel, den Entwickler dazu zu bringen, die Methode richtig aufzurufen.
    Oder anders: Welcher Depp wird denn einer Draw()-Methode, die einen Brush erwartet, Nothing übergeben und denken, das wird schon irgendwie?