|
10 | 10 | from its actual representation (generally for abstraction).
|
11 | 11 |
|
12 | 12 | *What does this example do?
|
13 |
| -This particular example uses a director function to abstract the |
14 |
| -construction of a building. The user specifies a Builder (House or |
15 |
| -Flat) and the director specifies the methods in the order necessary, |
16 |
| -creating a different building depending on the specification |
17 |
| -(from the Builder class). |
18 | 13 |
|
19 |
| -@author: Diogenes Augusto Fernandes Herminio <diofeher@gmail.com> |
20 |
| -https://gist.github.com/420905#file_builder_python.py |
| 14 | +The first example achieves this by using an abstract base |
| 15 | +class for a building, where the initializer (__init__ method) specifies the |
| 16 | +steps needed, and the concrete subclasses implement these steps. |
| 17 | +
|
| 18 | +In other programming languages, a more complex arrangement is sometimes |
| 19 | +necessary. In particular, you cannot have polymorphic behaviour in a |
| 20 | +constructor in C++ - see https://stackoverflow.com/questions/1453131/how-can-i-get-polymorphic-behavior-in-a-c-constructor |
| 21 | +- which means this Python technique will not work. The polymorphism |
| 22 | +required has to be provided by an external, already constructed |
| 23 | +instance of a different class. |
| 24 | +
|
| 25 | +In general, in Python this won't be necessary, but a second example showing |
| 26 | +this kind of arrangement is also included. |
21 | 27 |
|
22 | 28 | *Where is the pattern used practically?
|
23 | 29 |
|
|
29 | 35 | """
|
30 | 36 |
|
31 | 37 |
|
32 |
| -def construct_building(builder): |
33 |
| - builder.new_building() |
34 |
| - builder.build_floor() |
35 |
| - builder.build_size() |
36 |
| - return builder.building |
37 |
| - |
38 |
| - |
39 |
| -# Abstract Builder |
40 |
| -class Builder(object): |
| 38 | +# Abstract Building |
| 39 | +class Building(object): |
41 | 40 |
|
42 | 41 | def __init__(self):
|
43 |
| - self.building = None |
44 |
| - |
45 |
| - def new_building(self): |
46 |
| - self.building = Building() |
| 42 | + self.build_floor() |
| 43 | + self.build_size() |
47 | 44 |
|
48 | 45 | def build_floor(self):
|
49 | 46 | raise NotImplementedError
|
50 | 47 |
|
51 | 48 | def build_size(self):
|
52 | 49 | raise NotImplementedError
|
53 | 50 |
|
54 |
| -# Concrete Builder |
| 51 | + def __repr__(self): |
| 52 | + return 'Floor: {0.floor} | Size: {0.size}'.format(self) |
55 | 53 |
|
56 | 54 |
|
57 |
| -class BuilderHouse(Builder): |
| 55 | +# Concrete Buildings |
| 56 | +class House(Building): |
58 | 57 |
|
59 | 58 | def build_floor(self):
|
60 |
| - self.building.floor = 'One' |
| 59 | + self.floor = 'One' |
61 | 60 |
|
62 | 61 | def build_size(self):
|
63 |
| - self.building.size = 'Big' |
| 62 | + self.size = 'Big' |
64 | 63 |
|
65 | 64 |
|
66 |
| -class BuilderFlat(Builder): |
| 65 | +class Flat(Building): |
67 | 66 |
|
68 | 67 | def build_floor(self):
|
69 |
| - self.building.floor = 'More than One' |
| 68 | + self.floor = 'More than One' |
70 | 69 |
|
71 | 70 | def build_size(self):
|
72 |
| - self.building.size = 'Small' |
| 71 | + self.size = 'Small' |
73 | 72 |
|
74 | 73 |
|
75 |
| -# Product |
76 |
| -class Building(object): |
| 74 | +# In some very complex cases, it might be desirable to pull out the building |
| 75 | +# logic into another function (or a method on another class), rather than being |
| 76 | +# in the base class '__init__'. (This leaves you in the strange situation where |
| 77 | +# a concrete class does not have a useful constructor) |
77 | 78 |
|
78 |
| - def __init__(self): |
79 |
| - self.floor = None |
80 |
| - self.size = None |
81 | 79 |
|
| 80 | +class ComplexBuilding(object): |
82 | 81 | def __repr__(self):
|
83 | 82 | return 'Floor: {0.floor} | Size: {0.size}'.format(self)
|
84 | 83 |
|
85 | 84 |
|
| 85 | +class ComplexHouse(ComplexBuilding): |
| 86 | + def build_floor(self): |
| 87 | + self.floor = 'One' |
| 88 | + |
| 89 | + def build_size(self): |
| 90 | + self.size = 'Big and fancy' |
| 91 | + |
| 92 | + |
| 93 | +def construct_building(cls): |
| 94 | + building = cls() |
| 95 | + building.build_floor() |
| 96 | + building.build_size() |
| 97 | + return building |
| 98 | + |
| 99 | + |
86 | 100 | # Client
|
87 | 101 | if __name__ == "__main__":
|
88 |
| - building = construct_building(BuilderHouse()) |
89 |
| - print(building) |
90 |
| - building = construct_building(BuilderFlat()) |
91 |
| - print(building) |
| 102 | + house = House() |
| 103 | + print(house) |
| 104 | + flat = Flat() |
| 105 | + print(flat) |
| 106 | + |
| 107 | + # Using an external constructor function: |
| 108 | + complex_house = construct_building(ComplexHouse) |
| 109 | + print(complex_house) |
92 | 110 |
|
93 | 111 | ### OUTPUT ###
|
94 | 112 | # Floor: One | Size: Big
|
95 | 113 | # Floor: More than One | Size: Small
|
| 114 | +# Floor: One | Size: Big and fancy |
0 commit comments