Pin0 schreef op vrijdag 17 april 2015 @ 16:29:
Wij slaan in onze webwinkel bij de orders een copy op van de producten. Voor de overzichten, facturen en andere berekeningen gebruiken we de waarden uit het copy object. We kunnen dan altijd de prijs in de database aanpassen zonder dat de order history wordt beïnvloed.
Dat moet je sowieso altijd doen. Maar als je wil kunnen variëren tussen berekenings
wijzen (en dus niet alleen maar bedragen) dan zul je
dus prijzen van die datums moeten hebben omdat je mogelijk een 'PriceCalculator' gebruikt die niet alle benodigde values in je "copy object" kan vinden (CalcA gebruikt bedragen A, B en C om een totaal te berekenen, CalcB gebruikt A, B en Z bijvoorbeeld; in je copy object zal logischerwijs enkel A, B en C aanwezig zijn (of je slaat bij elke factuur je complete prijzenstructuur op

)).
Ik zie niet waarom
$numberOfPrints in de constructor van de calculators meegegeven wordt (en dus ook aan de factory meegegeven moet worden). Waarom is dat geen argument voor de
getPrice() method? This makes much more sense:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| <?php
interface PriceCalculatorInterface
{
public function getPrice($numberOfPrints);
}
class PriceCalculatorFactory {
public static function create($year)
{
switch ($year) {
case 2016:
return new PriceCalculator2016();
case 2015:
return new PriceCalculator2015();
default:
throw new Exception('Unknown price calculator!');
}
}
}
class PriceCalculator2015 implements PriceCalculatorInterface {
public function getPrice($numberOfPrints)
{
return $this->getSetupPrice()
+ $this->getPaperPrice($numberOfPrints)
+ $this->getInkPrice($numberOfPrints);
}
private function getSetupPrice()
{
return 125;
}
private function getPaperPrice($numberOfPrints)
{
return $numberOfPrints * 0.09;
}
private function getInkPrice($numberOfPrints)
{
return $numberOfPrints * 0.03;
}
}
class PriceCalculator2016 implements PriceCalculatorInterface {
public function getPrice($numberOfPrints)
{
return $numberOfPrints * 0.15;
}
} |
offtopic:
BTW:
break;s na een return zijn niet nodig

Overigens is dit nog steeds een sterk vereendvoudigd voorbeeld; ik zou bijvoorbeeld
PHP:
1
2
3
4
| interface PriceCalculatorInterface
{
public function getPrice($numberOfPrints);
} |
niet heel gauw doen; ik zou
getPrice() eerder een
$orderline argument o.i.d. meegeven; nu zit je vast aan een enkele "int" (voor zover type-safety in PHP uberhaupt een ding is...) namelijk
$numberOfPrints waar
getPrice() het maar mee moet doen. Als je
getPrice() een
object (zoals orderline, order of
foobar) meegeeft ben je véél flexibeler later in je calculators als PriceCalculator2015 z'n prijs baseert op aantallen en PriceCalculator2017 op andere gegevens die onderdeel zijn van de order(regel) zoals papierkleur, full-color vs. b/w druk etc.
Wat voor object je
precies meegeeft (en of "getPrice()" uberhaupt wel zo'n goede keuze is) is afhankelijk van wat je nu hebt / wat je structuur is etc. maar je kunt je zoiets voorstellen:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
| class PriceCalculator2015 implements PriceCalculatorInterface {
public function getPrice($orderline)
{
return $this->getSetupPrice()
+ $this->getPaperPrice($orderline->numberOfPrints)
+ $this->getInkPrice($orderline->numberOfPrints);
}
private function getSetupPrice()
{
return 125;
}
private function getPaperPrice($numberOfPrints)
{
return $numberOfPrints * 0.09;
}
private function getInkPrice($numberOfPrints)
{
return $numberOfPrints * 0.03;
}
}
class PriceCalculator2016 implements PriceCalculatorInterface {
public function getPrice($orderline)
{
return $orderline->numberOfPrints * 0.15;
}
}
//New class! YAY!
class PriceCalculator2017 implements PriceCalculatorInterface {
public function getPrice($orderline)
{
return $this->getSetupPrice()
+ $this->getPaperPrice($orderline->numberOfPrints, $orderline->paperColor)
+ $this->getInkPrice($orderline->numberOfPrints, $orderline->printType);
}
private function getSetupPrice()
{
return 50;
}
private function getPaperPrice($numberOfPrints, $paperColor)
{
switch ($paperColor) {
case 'white':
return $numberOfPrints * 0.09;
case 'pink':
return $numberOfPrints * 0.10;
case 'yellow':
return $numberOfPrints * 0.15;
default:
throw new Exception('Unknown papercolor!');
}
}
private function getInkPrice($numberOfPrints, $printType)
{
switch ($printType) {
case 'b/w':
return $numberOfPrints * 0.03;
case '2-tone':
return $numberOfPrints * 0.08;
case 'full-color':
return $numberOfPrints * 0.15;
default:
throw new Exception('Unknown printtype!');
}
}
} |
Als je dan in 2018 een nieuwe property
paperWeight toevoegt aan een orderline (order, foobar) object dan blijven je oude calculators gewoon werken en kan je 2018 calculator gebruik maken van dit nieuwe gegeven om een prijs op te baseren.
Tot slot: Het is in de meeste talen gebruikelijk een interface te prefixen met I<something>; ik zou dus geen PriceCalculatorInterface doen maar IPriceCalculator. Laat maar, in PHP lijkt (op 't eerste gezicht na een korte google opdracht althans) een
Interface-suffix gangbaar(der) te zijn dan een
I-prefix.
[
Voor 93% gewijzigd door
RobIII op 17-04-2015 19:28
]
There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.
Je eigen tweaker.me redirect
Over mij