Strategy Pattern
Coderefaktoring
davor
Der folgende Beispielcode besteht fast nur aus case Anweisungen, die vom Beschäftigungsverhältnis abhängig sind.Er ermittelt, abhängig vom Eingabewert, die wichtigen Dinge im Leben, wie den Jahresurlaub, die Prämien, die Wochensarbeitszeit und den Lohn.
Code zeigen Er schmeckt gewaltig.
public class strategyFirst {
public static void main(String[] args){
if ( args.length != 1){
System.out.println("Usage: java strategyFirst number ");
System.exit(0);
}
int type= Integer.parseInt(args[0]);
System.out.println("Annual Vacation: " + getAnnualVacation( type));
System.out.println("Bonnification: "+ getBonification( type ) );
System.out.println("WeeklyWorkingHourse: "+ getWeeklyWorkingHours(type));
System.out.println("Loan: " + getLoan( type ) );
}
private static double getBonification( int type ){
// something to do
double bonUnit= 1000;
switch ( type ){
// boss
case 0:
return 10* bonUnit;
// manager
case 1:
return 2* bonUnit;
//officer
case 2:
return bonUnit;
// employee + rest
default:
return 0;
}
}
private static int getWeeklyWorkingHours( int type ){
// something to do
int weekUnit=60;
switch ( type ){
// boss
case 0:
return weekUnit - 40;
// manager
case 1:
return weekUnit - 20;
//officer
case 2:
return weekUnit;
// employee
case 3:
return weekUnit + 10;
//rest
default:
return weekUnit + 20;
}
}
private static double getAnnualVacation( int type ){
// something to do
double dayUnit= 20;
switch ( type ){
// boss
case 0:
return 3* dayUnit;
// manager
case 1:
return 2 * dayUnit;
//officer
case 2:
return 1.5 * dayUnit;
// employee + rest
default:
return dayUnit;
}
}
private static double getLoan( int type ){
//something do do
double monthlyProfit= 100000;
double baseSalary= 1000;
switch ( type ){
// boss
case 0:
return 5 *baseSalary + 0.5 * monthlyProfit;
// manager
case 1:
return 3 * baseSalary + 0.1 * monthlyProfit;
//officer
case 2:
return 2* baseSalary;
//employee
case 3:
return baseSalary;
// rest
default:
return 0;
}
}
}
- Was kann man tun?
- Einführen einer Fabrikmethode, die mir zu dem angefragten Type das entsprechende Beschäftigunsverhältnis zurückgibt
- Parametrisieren der Algorithmen über den Typ des Beschäftigunsverhältnisses
- Heraus kam folgender Code - reduziert auf die Applikation - die die Strategien ( Arten des Beschäftigunsverhältnisses ) nutzt
danach
import strategySecond.Employment;
import strategySecond.Boss;
import strategySecond.Manager;
import strategySecond.Officer;
import strategySecond.Employee;
import strategySecond.Worker;
public class strategySecond {
public static void main(String[] args) {
if ( args.length != 1){
System.out.println("Usage: java strategyFirst number ");
System.exit(0);
}
Employment employ= getEmployment( Integer.parseInt(args[0]));
System.out.println("Annual Vacation: " + getAnnualVacation( employ));
System.out.println("Bonnification: "+ getBonification( employ ) );
System.out.println("WeeklyWorkingHourse: "+ getWeeklyWorkingHours(employ ));
System.out.println("Loan: " + getLoan( employ ) );
}
private static Employment getEmployment( int type ){
switch ( type ){
// boss
case 0:
return new Boss();
// manager
case 1:
return new Manager();
//officer
case 2:
return new Officer();
//employee
case 3:
return new Employee();
// rest
default:
return new Worker();
}
}
private static double getBonification( Employment employ ){
//something to do
return employ.getBonification();
}
private static int getWeeklyWorkingHours( Employment employ ){
//something to do
return employ.getWeeklyWorkingHours();
}
private static double getAnnualVacation( Employment employ ){
//something to do
return employ.getAnnualVacation();
}
private static double getLoan( Employment employ ){
//something to do
return employ.getLoan();
}
}
- die verschiedenen Strategien kann man einfacher mit einem Klassendiagramm darstellen
- Beschäftigunsverhältnisse:
Klassische Sicht
- da neben dem Strategie Pattern bei dem obigen Code nach anderen Pattern zur Verwirrung beigetragen haben, will ich das Strategie Pattern noch in seiner klassischen Form beschreiben
Anwendbarkeit
- viele verwandte Klassen unterscheiden sich nur in ihrem Verhalten
- unterschiedliche Verhaltensweisen eines Alogrithmus sollen unterstützt werden
Lösung
- Kapsle die unterschiedlichen Verhaltensweisen in einem Objekt, mit dem du die Methode/Klasse parametrisieren kannst
klassisches Beispiel
- Sort Algorithmus:
Konsequenzen
- Alternative zur Unterklassenbildung, insbesondere zur Template Methode
- das Allheilmittel gegen bedingtes Programmieren
- der Klient kann zu Laufzeit ( Unterschied zur Template Methode ) seine Strategie wechseln
- jede Strategie verlangt einen neuen Strategiehierachie
Variante
- das Stratgiepattern wird auch gern Policy oder Traits bezeichnet
Traits
- Insbesondere Traits betonen aber nicht das Parametrisieren von Algorithmen, sondern das Parametrisiern von Typen.
- Die Strukturmittel um diese Varianz im Verhalten zu erreichen sind die oben beschriebenen Strukturmittel, nur die Intention ist eine andere.
- Bekannte Beispiele sind die Container der STL in C++ aber auch insbesondere:
std::string und std::wstring
typedef basic_string<char> string;
typedef basic_string<wchar> wstring;
template<class Ch, class Tr= char_traits<Ch>, class A= allocator<Ch> >
class std::basic_string{
...
};
- std::string ist ein typedef auf basic_string, der mit dem Charakter char parametrisiert wurde
- dieser Charaktertype char gibt die Defaultwerte für seine Kernfunktionalität als Zeichen und seine Allokierungsstrategie vor
- implementiert man für seinen Charakteryp
char_traits
undallocator<>MyChar>
, so spricht nicht gegen den neuen, eventuell lokalisierten String
typedef basic_string<Mychar> MyString;
- gerade durch das generische Programmieren mittels Templates ist es möglich, die Varianz des Verhaltens schon zur Compilezeit zu bestimmen, so daß zur Laufzeit kein virtueller Dispatch mehr notwendig ist
Weiterlesen...