AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweitert.
Thema durchsuchen
Ansicht
Themen-Optionen

Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweitert.

Offene Frage von "Stevie"
Ein Thema von Der schöne Günther · begonnen am 8. Sep 2014 · letzter Beitrag vom 8. Sep 2014
Antwort Antwort
Seite 1 von 2  1 2      
Der schöne Günther

Registriert seit: 6. Mär 2013
6.159 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweitert.

  Alt 8. Sep 2014, 17:33
Es geht mir um einen Punkt von Bernd Uas Vortrag "Besseren Code mit Delphi" von den Delphi-Tagen 2014:

Einen seiner letzten Punkte habe ich nicht verstanden:

Wenn man, beispielsweise mittels case -Anweisung, einen Enumerationstypen überprüft sollte man sich die Möglichkeit offen lassen, in einer Unterklasse auch auf neu hinzugekommene Typen zu reagieren. Wie soll das gehen?

Im folgenden ein TStadtMusikantenChecker der prüft, ob ein TAnimal zu den Bremer Stadtmusikanten gehört: Er prüft nur auf Hund und Maus. Später wurde TAnimal um eine Katze erweitert. Dafür wurde dann die Ableitung TStadtMusikantenCheckerSub = class(TStadtMusikantenChecker) gebildet:

Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses System.SysUtils;

type
   TAnimal = (hund, katze, maus);

   TStadtMusikantenChecker = class
      public class function checkIsInBremerStadtMusikanten(const animal: TAnimal): Boolean; virtual;
      protected class function checkIsInBremerStadtMusikantenHook(const animal: TAnimal): Boolean; virtual;
   end;

   TStadtMusikantenCheckerSub = class(TStadtMusikantenChecker)
      protected class function checkIsInBremerStadtMusikantenHook(const animal: TAnimal): Boolean; override;
    end;



class function TStadtMusikantenChecker.checkIsInBremerStadtMusikanten(const animal: TAnimal): Boolean;
begin
   case animal of
      TAnimal.hund: Result := True;
      TAnimal.maus: Result := False
   else
      //raise EArgumentOutOfRangeException.Create(EmptyStr);
      Result := checkIsInBremerStadtMusikantenHook(animal);
   end;
end;

class function TStadtMusikantenChecker.checkIsInBremerStadtMusikantenHook(const animal: TAnimal): Boolean;
begin
   raise EArgumentOutOfRangeException.Create(EmptyStr);
end;

class function TStadtMusikantenCheckerSub.checkIsInBremerStadtMusikantenHook(const animal: TAnimal): Boolean;
begin
   case animal of
      TAnimal.katze: Result := True;
   else
      Result := inherited;
   end;
end;


const
   animal: TAnimal = TAnimal.hund;

begin
   try
      WriteLn( TStadtMusikantenChecker.checkIsInBremerStadtMusikanten(animal) );
   except
      on E: Exception do
         Writeln(E.ClassName, ': ', E.Message);
   end;

   ReadLn;
end.
War so etwas gemeint? Oder habe ich etwas falsch verstanden?
  Mit Zitat antworten Zitat
nuclearping

Registriert seit: 7. Jun 2008
708 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#2

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 17:49
Wenn, dann WriteLn( TStadtMusikantenCheckerSub.checkIsInBremerStadtMusikanten(animal) ); . Sonst weiss ja TStadtMusikantenChecker nichts von der Existenz von TStadtMusikantenCheckerSub und die neue Funktion wird nicht aufgerufen. Oder hab ich da was falsch verstanden?

Obgleich sich mir nicht der Sinn erschließt, warum man dafür eine extra Unterklasse implementieren soll, wenn man die Enum erweitert? Soll das gegeben dem Fall sein, dass man keinen Zugriff auf den Originalcode hat? Bzw. nicht darin "rumwurschteln" braucht?
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.159 Beiträge
 
Delphi 10 Seattle Enterprise
 
#3

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 18:06
Wenn, dann TStadtMusikantenCheckerSub
Ja, natürlich. Im geposteten Code noch vollkommen ok, denn es ist ja ein Hund

Zitat:
Obgleich sich mir nicht der Sinn erschließt, warum man dafür eine extra Unterklasse implementieren soll [...]nicht darin "rumwurschteln"?
Deshalb frage ich ja. Ist aber, finde ich, schon ein Argument dafür. Die Unterklasse ist dann wie ein kleiner Helfer der die Oberklasse um eine detailliertere Behandlung "erweitert".

Habe ich bislang jedenfalls noch nie so gemacht
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.016 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#4

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 18:14
Bei sowas sind enums zu vermeiden und Vererbung/Polymorphie zu nutzen (z.B. Strategie oder Kommando Pattern)

Wenn ich das richtig in Erinnerung habe, dann ging es um das TCustomer Beispiel und den jeweiligen Rabatt. In solch einem Fall hab ich verschiedene TCustomer Nachfahren, die jeweils die GetRabatt Methode überschreiben.

In diesem Zusammenhang finde ich diesen Clean Code Talk sehr erhellend.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.453 Beiträge
 
Delphi 12 Athens
 
#5

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 18:22
Das hast du noch nicht ganz richtig verstanden. Da deine Methode eine Eigenschaft des übergebenen Parameters bzw. die Zugehörigkeit zu einer Gruppe prüft, ist erstens die case-Anweisung an sich fraglich und zweitens eine Ableitung wegen einer vergrößerten Testmenge auch nicht gerade pfiffig. Ohne jetzt weiter auf die adäquate Lösung des Musikantenproblems einzugehen, lieber mal ein Beispiel für das was Bernd eigentlich meint.

Wenn das case-Konstrukt allein dafür da ist, unterschiedliche Implementationen aufzurufen, ist es eigentlich ratsamer für jede Implementation eine abgeleitete Klasse zu schreiben und dann je nach gewünschter Implementation die passende Klasse zu instantiieren. Dabei verlagert man dann den case-Konstrukt aus der Implementationsauswahl an die Stelle, wo die richtige Klasse ausgewählt werden muss. Die Idee dahinter ist eigentlich die, daß eine Klasse nur für eine bestimmte Aufgabe bestimmt sein und nicht mehrere Aufgaben zusammenfassen soll. Bei einer neuen Aufgabe erweitert man dann nicht die Funktionalität der einen Klasse sondern schreibt eine eigene.

Würde man das Musikantenproblem so implementieren, dann würde man eine Basis-Klasse schreiben, die eine

function checkIsInBremerStadtMusikanten: Boolean; virtual; abstract;

deklariert, und davon für jedes mögliche Tier eine Ableitung bereitstellt, die diese Funktion entsprechend implementiert. Je nach abzufragendem Tier verwendest du dann die entsprechende Klasse. Das ist natürlich blanker Unsinn und man sieht dabei schön, daß man ein Clean Code Prinzip nicht blind auf alle möglichen Fälle anwenden sollte.

Für diesen konkreten Fall wäre ein set of TAnimal die bessere Wahl.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#6

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 18:23
Na in dem Beispiel mit den Tieren aus Bremen wäre das Case-Switch imho noch annehmbar. Deswegen gleich eine Klassenfamilie aufzumachen, wäre dann doch etwas Overkill. Lesbar soll das ja noch bleiben. Aber der Hinweis auf die Pattern ist sicherlich hilfreich (Sobald eine zweites Case ins Spiel kommt oder etwas mehr passiert, als einen Return-Wert zu setzen, sollte man aber ansetzen).

Abschließend vielleicht noch das Stichwort: Factory. Das ist das, was Uwe und Stevie meinen. In der (Klassen)Fabrik wird anhand einer Eigenschaft der Spezialist (also die Klasse) ausgewählt, der die Verarbeitung übernimmt.

Einfaches Beispiel: Pizza.
Delphi-Quellcode:
Type
  TPizza = (Margeritha, Funghi, QuatroStattione, MitWienerWurstUndSenf);

// Anstatt
Case Pizza of
  Margeritha: begin PackdieTomatesoßerauf; UndKäse; end;
  Funghi : begin PackdieTomatesoßerauf; Pilze; UndKäse; end;
  ...
...
  end;
//schreibt man
  PizzaFactory.CreateRecipe(Pizza).BelegDiePizza();
// Und dieser Code ändert sich dann niemals wieder.
Man stelle sich vor, aus der kleinen Klitsche, die 4 Pizzas macht, wird mal eine, die 30 Sorten kann. Dann würde das Case-Statement ziemlich schrottig aussehen, wohingegen bei der Factory alles immer noch so aufgeräumt ist, wie am ersten Tag.

Merke: Mit sauberem Code kaschiert man auch das eigene Chaos (mach ich jedenfalls so)

Geändert von Dejan Vu ( 8. Sep 2014 um 18:39 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.016 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#7

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 18:36
Bei den Kunden mit den Rabatten würde ich mir auch 2x überlegen, ob ich dafür Ableitungen von Kunden machen würde. Erstens sind das immer noch Kunden und nur weil der heute mal 20% Rabatt bekommt (Geburtstag!) ist das doch kein anderer Kunde, und außerdem würde für den Rabatt wieder eine Strategy passen, unter der Annahme, das es sich bei Rabatt um etwas komplexeres handelt, als nur eine einzelne Zahl.

Ich stelle mir auch lustig vor, dem Kunden zur Laufzeit einen anderen Rabatt zu verpassen: Da müsste ich den Kunden komplett zerstören (also seine Instanz, nicht ihn), nur weil heute sein Geburtstag ist.. Nun ja.
In deinem Beispiel besteht die Rabattberechnung aus mehreren Teilen, der erste Teil, der sich über den Typ des Kunden ergibt (normalo, silber, gold...), also der Rabatt, den er immer bekommt und zusätzlich ich nenns mal temporären Rabatt (weil er Geburtstag hat oder Deutschland 7 Tore geschossen hat, oder...).

Natürlich hinkt das Beispiel wie es Beispiele nunmal tun, denn der flexibelste Weg wäre eine Rabatt Property. Die ermöglicht nämlich auch individuell ausgehandelten Rabatt, den man z.b. in seiner Datenbank pflegt. Aber darum gehts ja in dem Fall nicht, sondern darum, Conditions über Polymorphie zu lösen.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie ( 8. Sep 2014 um 18:39 Uhr)
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#8

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 18:42
Na, ich würde auch dann keinen Kunden ableiten, sondern die Strategy verwenden (mit der Factory, fertig).
  Mit Zitat antworten Zitat
nuclearping

Registriert seit: 7. Jun 2008
708 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#9

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 19:06
Danke für die Erklärungen. Werde mir das Video mal in Ruhe anschauen. Bei sowas lernt man ja (wie man sieht) nie aus.
  Mit Zitat antworten Zitat
sahimba

Registriert seit: 14. Nov 2011
Ort: Berlin, Hauptstadt der DDR
137 Beiträge
 
Delphi 10 Seattle Professional
 
#10

AW: Case-Anweisung auf Enumerationstyp. Eines Tages wird der Enumerationstyp erweiter

  Alt 8. Sep 2014, 19:07
In solch einem Fall hab ich verschiedene TCustomer Nachfahren, die jeweils die GetRabatt Methode überschreiben.
Da fiele mir allerdings als erstes das Strategy Pattern ein.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:09 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz