1class BuildingMaterial:
2 """Represents a generic building material."""
3
4 def __init__(self, mass, factory=None):
5 """
6 Initialize a BuildingMaterial instance.
7
8 Args:
9 mass (float): The mass of the material.
10 factory (Factory, optional): The factory that created the material.
11 """
12 self._mass = mass
13 self._used = False
14 self._factory = factory
15
16 @property
17 def mass(self):
18 return self._mass
19
20 @property
21 def factory(self):
22 return self._factory
23
24 @factory.setter
25 def factory(self, other_factory):
26 self._factory = other_factory
27
28 @property
29 def volume(self):
30 """Calculate the volume of the material based on its density."""
31 return self.mass / self.density
32
33
34class Concrete(BuildingMaterial):
35 """Represents concrete with a specific density."""
36
37 density = 2500
38
39
40class Brick(BuildingMaterial):
41 """Represents brick with a specific density."""
42
43 density = 2000
44
45
46class Stone(BuildingMaterial):
47 """Represents stone with a specific density."""
48
49 density = 1600
50
51
52class Wood(BuildingMaterial):
53 """Represents wood with a specific density."""
54
55 density = 600
56
57
58class Steel(BuildingMaterial):
59 """Represents steel with a specific density."""
60
61 density = 7700
62
63
64class Factory:
65 """Represents a factory that produces building materials and alloys."""
66
67 _VALID_MATERIALS = {
68 "Concrete": Concrete,
69 "Brick": Brick,
70 "Stone": Stone,
71 "Wood": Wood,
72 "Steel": Steel
73 }
74
75 _created_alloy_types = {}
76 _total_volume_sum = 0
77
78 def __init__(self):
79 """Initialize a Factory instance."""
80 self._instance_volume_sum = 0
81
82 def _decrease_volume_sums(self, vol):
83 self._instance_volume_sum -= vol
84 Factory._total_volume_sum -= vol
85
86 def _increase_volume_sums(self, vol):
87 self._instance_volume_sum += vol
88 Factory._total_volume_sum += vol
89
90 def _create_single_material(self, material_name, mass):
91 if material_name in Factory._VALID_MATERIALS:
92 return Factory._VALID_MATERIALS[material_name](mass, self)
93 elif material_name in Factory._created_alloy_types:
94 return Factory._created_alloy_types[material_name](mass, self)
95 else:
96 raise ValueError("Keyword arguments must have valid materials as keys!")
97
98 def _create_materials(self, **kwargs):
99 result = [self._create_single_material(key, value) for key, value in kwargs.items()]
100 self._increase_volume_sums(sum(m.volume for m in result))
101 return tuple(result)
102
103 @staticmethod
104 def _generate_alloy_name(materials):
105 names = []
106 for material in set(materials):
107 names.extend(material.__class__.__name__.split("_"))
108 return "_".join(sorted(names))
109
110 @classmethod
111 def _add_new_alloy_type(cls, alloy_name):
112 material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")]
113 avg_density = sum(m.density for m in material_types) / len(material_types)
114 cls._created_alloy_types[alloy_name] = type(
115 alloy_name,
116 (BuildingMaterial,),
117 {
118 "density": avg_density,
119 }
120 )
121
122 @staticmethod
123 def _invalidate_materials(materials):
124 for m in materials:
125 m._used = True
126 if m.factory:
127 m.factory._decrease_volume_sums(m.volume)
128 m.factory = None
129
130 def _create_dynamic_alloy(self, *materials):
131 for m in materials:
132 if m._used:
133 raise AssertionError("BuildingMaterial objects can't be reused!")
134
135 alloy_name = Factory._generate_alloy_name(materials)
136 mass_sum = sum(m.mass for m in materials)
137
138 if alloy_name not in Factory._created_alloy_types:
139 Factory._add_new_alloy_type(alloy_name)
140
141 Factory._invalidate_materials(materials)
142
143 alloy = Factory._created_alloy_types[alloy_name](mass_sum, self)
144 self._increase_volume_sums(alloy.volume)
145 return alloy
146
147 @staticmethod
148 def _validate_call_arguments(*args, **kwargs):
149 if args and kwargs:
150 raise ValueError("Factory can't be called with both positional and keyword arguments!")
151 if not args and not kwargs:
152 raise ValueError("Factory object can't be called without arguments!")
153
154 def __call__(self, *args, **kwargs):
155 """
156 Create materials or an alloy using the factory.
157
158 Args:
159 *args: Positional arguments for dynamic alloys.
160 **kwargs: Keyword arguments for creating materials.
161 """
162 Factory._validate_call_arguments(*args, **kwargs)
163 if kwargs:
164 return self._create_materials(**kwargs)
165 elif args:
166 alloy = self._create_dynamic_alloy(*args)
167 return alloy
168
169 def can_build(self, volume_target):
170 """Check if the factory can build with the specified volume."""
171 return self._instance_volume_sum >= volume_target
172
173 @classmethod
174 def can_build_together(cls, volume_target):
175 """Check if all factories combined can build with the specified volume."""
176 return cls._total_volume_sum >= volume_target
.......F..
======================================================================
FAIL: test_positional_arguments_single_argument (test.TestFactory.test_positional_arguments_single_argument)
Test calling a factory using a sigle positional argument.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 80, in test_positional_arguments_single_argument
self.assertIs(type(concrete2), solution.Concrete)
AssertionError: <class 'solution.Concrete'> is not <class 'solution.Concrete'>
----------------------------------------------------------------------
Ran 10 tests in 0.015s
FAILED (failures=1)
Виктор Бечев
23.11.2024 23:08Хубаво разделение на функции и като цяло чист код, браво.
|
f | 1 | class BuildingMaterial: | f | 1 | class BuildingMaterial: |
2 | """Represents a generic building material.""" | 2 | """Represents a generic building material.""" | ||
3 | 3 | ||||
4 | def __init__(self, mass, factory=None): | 4 | def __init__(self, mass, factory=None): | ||
5 | """ | 5 | """ | ||
6 | Initialize a BuildingMaterial instance. | 6 | Initialize a BuildingMaterial instance. | ||
7 | 7 | ||||
8 | Args: | 8 | Args: | ||
9 | mass (float): The mass of the material. | 9 | mass (float): The mass of the material. | ||
10 | factory (Factory, optional): The factory that created the material. | 10 | factory (Factory, optional): The factory that created the material. | ||
11 | """ | 11 | """ | ||
12 | self._mass = mass | 12 | self._mass = mass | ||
13 | self._used = False | 13 | self._used = False | ||
n | 14 | self.factory = factory | n | 14 | self._factory = factory |
15 | 15 | ||||
16 | @property | 16 | @property | ||
17 | def mass(self): | 17 | def mass(self): | ||
18 | return self._mass | 18 | return self._mass | ||
t | t | 19 | |||
20 | @property | ||||
21 | def factory(self): | ||||
22 | return self._factory | ||||
23 | |||||
24 | @factory.setter | ||||
25 | def factory(self, other_factory): | ||||
26 | self._factory = other_factory | ||||
19 | 27 | ||||
20 | @property | 28 | @property | ||
21 | def volume(self): | 29 | def volume(self): | ||
22 | """Calculate the volume of the material based on its density.""" | 30 | """Calculate the volume of the material based on its density.""" | ||
23 | return self.mass / self.density | 31 | return self.mass / self.density | ||
24 | 32 | ||||
25 | 33 | ||||
26 | class Concrete(BuildingMaterial): | 34 | class Concrete(BuildingMaterial): | ||
27 | """Represents concrete with a specific density.""" | 35 | """Represents concrete with a specific density.""" | ||
28 | 36 | ||||
29 | density = 2500 | 37 | density = 2500 | ||
30 | 38 | ||||
31 | 39 | ||||
32 | class Brick(BuildingMaterial): | 40 | class Brick(BuildingMaterial): | ||
33 | """Represents brick with a specific density.""" | 41 | """Represents brick with a specific density.""" | ||
34 | 42 | ||||
35 | density = 2000 | 43 | density = 2000 | ||
36 | 44 | ||||
37 | 45 | ||||
38 | class Stone(BuildingMaterial): | 46 | class Stone(BuildingMaterial): | ||
39 | """Represents stone with a specific density.""" | 47 | """Represents stone with a specific density.""" | ||
40 | 48 | ||||
41 | density = 1600 | 49 | density = 1600 | ||
42 | 50 | ||||
43 | 51 | ||||
44 | class Wood(BuildingMaterial): | 52 | class Wood(BuildingMaterial): | ||
45 | """Represents wood with a specific density.""" | 53 | """Represents wood with a specific density.""" | ||
46 | 54 | ||||
47 | density = 600 | 55 | density = 600 | ||
48 | 56 | ||||
49 | 57 | ||||
50 | class Steel(BuildingMaterial): | 58 | class Steel(BuildingMaterial): | ||
51 | """Represents steel with a specific density.""" | 59 | """Represents steel with a specific density.""" | ||
52 | 60 | ||||
53 | density = 7700 | 61 | density = 7700 | ||
54 | 62 | ||||
55 | 63 | ||||
56 | class Factory: | 64 | class Factory: | ||
57 | """Represents a factory that produces building materials and alloys.""" | 65 | """Represents a factory that produces building materials and alloys.""" | ||
58 | 66 | ||||
59 | _VALID_MATERIALS = { | 67 | _VALID_MATERIALS = { | ||
60 | "Concrete": Concrete, | 68 | "Concrete": Concrete, | ||
61 | "Brick": Brick, | 69 | "Brick": Brick, | ||
62 | "Stone": Stone, | 70 | "Stone": Stone, | ||
63 | "Wood": Wood, | 71 | "Wood": Wood, | ||
64 | "Steel": Steel | 72 | "Steel": Steel | ||
65 | } | 73 | } | ||
66 | 74 | ||||
67 | _created_alloy_types = {} | 75 | _created_alloy_types = {} | ||
68 | _total_volume_sum = 0 | 76 | _total_volume_sum = 0 | ||
69 | 77 | ||||
70 | def __init__(self): | 78 | def __init__(self): | ||
71 | """Initialize a Factory instance.""" | 79 | """Initialize a Factory instance.""" | ||
72 | self._instance_volume_sum = 0 | 80 | self._instance_volume_sum = 0 | ||
73 | 81 | ||||
74 | def _decrease_volume_sums(self, vol): | 82 | def _decrease_volume_sums(self, vol): | ||
75 | self._instance_volume_sum -= vol | 83 | self._instance_volume_sum -= vol | ||
76 | Factory._total_volume_sum -= vol | 84 | Factory._total_volume_sum -= vol | ||
77 | 85 | ||||
78 | def _increase_volume_sums(self, vol): | 86 | def _increase_volume_sums(self, vol): | ||
79 | self._instance_volume_sum += vol | 87 | self._instance_volume_sum += vol | ||
80 | Factory._total_volume_sum += vol | 88 | Factory._total_volume_sum += vol | ||
81 | 89 | ||||
82 | def _create_single_material(self, material_name, mass): | 90 | def _create_single_material(self, material_name, mass): | ||
83 | if material_name in Factory._VALID_MATERIALS: | 91 | if material_name in Factory._VALID_MATERIALS: | ||
84 | return Factory._VALID_MATERIALS[material_name](mass, self) | 92 | return Factory._VALID_MATERIALS[material_name](mass, self) | ||
85 | elif material_name in Factory._created_alloy_types: | 93 | elif material_name in Factory._created_alloy_types: | ||
86 | return Factory._created_alloy_types[material_name](mass, self) | 94 | return Factory._created_alloy_types[material_name](mass, self) | ||
87 | else: | 95 | else: | ||
88 | raise ValueError("Keyword arguments must have valid materials as keys!") | 96 | raise ValueError("Keyword arguments must have valid materials as keys!") | ||
89 | 97 | ||||
90 | def _create_materials(self, **kwargs): | 98 | def _create_materials(self, **kwargs): | ||
91 | result = [self._create_single_material(key, value) for key, value in kwargs.items()] | 99 | result = [self._create_single_material(key, value) for key, value in kwargs.items()] | ||
92 | self._increase_volume_sums(sum(m.volume for m in result)) | 100 | self._increase_volume_sums(sum(m.volume for m in result)) | ||
93 | return tuple(result) | 101 | return tuple(result) | ||
94 | 102 | ||||
95 | @staticmethod | 103 | @staticmethod | ||
96 | def _generate_alloy_name(materials): | 104 | def _generate_alloy_name(materials): | ||
97 | names = [] | 105 | names = [] | ||
98 | for material in set(materials): | 106 | for material in set(materials): | ||
99 | names.extend(material.__class__.__name__.split("_")) | 107 | names.extend(material.__class__.__name__.split("_")) | ||
100 | return "_".join(sorted(names)) | 108 | return "_".join(sorted(names)) | ||
101 | 109 | ||||
102 | @classmethod | 110 | @classmethod | ||
103 | def _add_new_alloy_type(cls, alloy_name): | 111 | def _add_new_alloy_type(cls, alloy_name): | ||
104 | material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")] | 112 | material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")] | ||
105 | avg_density = sum(m.density for m in material_types) / len(material_types) | 113 | avg_density = sum(m.density for m in material_types) / len(material_types) | ||
106 | cls._created_alloy_types[alloy_name] = type( | 114 | cls._created_alloy_types[alloy_name] = type( | ||
107 | alloy_name, | 115 | alloy_name, | ||
108 | (BuildingMaterial,), | 116 | (BuildingMaterial,), | ||
109 | { | 117 | { | ||
110 | "density": avg_density, | 118 | "density": avg_density, | ||
111 | } | 119 | } | ||
112 | ) | 120 | ) | ||
113 | 121 | ||||
114 | @staticmethod | 122 | @staticmethod | ||
115 | def _invalidate_materials(materials): | 123 | def _invalidate_materials(materials): | ||
116 | for m in materials: | 124 | for m in materials: | ||
117 | m._used = True | 125 | m._used = True | ||
118 | if m.factory: | 126 | if m.factory: | ||
119 | m.factory._decrease_volume_sums(m.volume) | 127 | m.factory._decrease_volume_sums(m.volume) | ||
120 | m.factory = None | 128 | m.factory = None | ||
121 | 129 | ||||
122 | def _create_dynamic_alloy(self, *materials): | 130 | def _create_dynamic_alloy(self, *materials): | ||
123 | for m in materials: | 131 | for m in materials: | ||
124 | if m._used: | 132 | if m._used: | ||
125 | raise AssertionError("BuildingMaterial objects can't be reused!") | 133 | raise AssertionError("BuildingMaterial objects can't be reused!") | ||
126 | 134 | ||||
127 | alloy_name = Factory._generate_alloy_name(materials) | 135 | alloy_name = Factory._generate_alloy_name(materials) | ||
128 | mass_sum = sum(m.mass for m in materials) | 136 | mass_sum = sum(m.mass for m in materials) | ||
129 | 137 | ||||
130 | if alloy_name not in Factory._created_alloy_types: | 138 | if alloy_name not in Factory._created_alloy_types: | ||
131 | Factory._add_new_alloy_type(alloy_name) | 139 | Factory._add_new_alloy_type(alloy_name) | ||
132 | 140 | ||||
133 | Factory._invalidate_materials(materials) | 141 | Factory._invalidate_materials(materials) | ||
134 | 142 | ||||
135 | alloy = Factory._created_alloy_types[alloy_name](mass_sum, self) | 143 | alloy = Factory._created_alloy_types[alloy_name](mass_sum, self) | ||
136 | self._increase_volume_sums(alloy.volume) | 144 | self._increase_volume_sums(alloy.volume) | ||
137 | return alloy | 145 | return alloy | ||
138 | 146 | ||||
139 | @staticmethod | 147 | @staticmethod | ||
140 | def _validate_call_arguments(*args, **kwargs): | 148 | def _validate_call_arguments(*args, **kwargs): | ||
141 | if args and kwargs: | 149 | if args and kwargs: | ||
142 | raise ValueError("Factory can't be called with both positional and keyword arguments!") | 150 | raise ValueError("Factory can't be called with both positional and keyword arguments!") | ||
143 | if not args and not kwargs: | 151 | if not args and not kwargs: | ||
144 | raise ValueError("Factory object can't be called without arguments!") | 152 | raise ValueError("Factory object can't be called without arguments!") | ||
145 | 153 | ||||
146 | def __call__(self, *args, **kwargs): | 154 | def __call__(self, *args, **kwargs): | ||
147 | """ | 155 | """ | ||
148 | Create materials or an alloy using the factory. | 156 | Create materials or an alloy using the factory. | ||
149 | 157 | ||||
150 | Args: | 158 | Args: | ||
151 | *args: Positional arguments for dynamic alloys. | 159 | *args: Positional arguments for dynamic alloys. | ||
152 | **kwargs: Keyword arguments for creating materials. | 160 | **kwargs: Keyword arguments for creating materials. | ||
153 | """ | 161 | """ | ||
154 | Factory._validate_call_arguments(*args, **kwargs) | 162 | Factory._validate_call_arguments(*args, **kwargs) | ||
155 | if kwargs: | 163 | if kwargs: | ||
156 | return self._create_materials(**kwargs) | 164 | return self._create_materials(**kwargs) | ||
157 | elif args: | 165 | elif args: | ||
158 | alloy = self._create_dynamic_alloy(*args) | 166 | alloy = self._create_dynamic_alloy(*args) | ||
159 | return alloy | 167 | return alloy | ||
160 | 168 | ||||
161 | def can_build(self, volume_target): | 169 | def can_build(self, volume_target): | ||
162 | """Check if the factory can build with the specified volume.""" | 170 | """Check if the factory can build with the specified volume.""" | ||
163 | return self._instance_volume_sum >= volume_target | 171 | return self._instance_volume_sum >= volume_target | ||
164 | 172 | ||||
165 | @classmethod | 173 | @classmethod | ||
166 | def can_build_together(cls, volume_target): | 174 | def can_build_together(cls, volume_target): | ||
167 | """Check if all factories combined can build with the specified volume.""" | 175 | """Check if all factories combined can build with the specified volume.""" | ||
168 | return cls._total_volume_sum >= volume_target | 176 | return cls._total_volume_sum >= volume_target |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class BuildingMaterial: | f | 1 | class BuildingMaterial: |
2 | """Represents a generic building material.""" | 2 | """Represents a generic building material.""" | ||
3 | 3 | ||||
4 | def __init__(self, mass, factory=None): | 4 | def __init__(self, mass, factory=None): | ||
5 | """ | 5 | """ | ||
6 | Initialize a BuildingMaterial instance. | 6 | Initialize a BuildingMaterial instance. | ||
7 | 7 | ||||
8 | Args: | 8 | Args: | ||
9 | mass (float): The mass of the material. | 9 | mass (float): The mass of the material. | ||
10 | factory (Factory, optional): The factory that created the material. | 10 | factory (Factory, optional): The factory that created the material. | ||
11 | """ | 11 | """ | ||
12 | self._mass = mass | 12 | self._mass = mass | ||
13 | self._used = False | 13 | self._used = False | ||
n | 14 | self._factory = factory | n | 14 | self.factory = factory |
15 | 15 | ||||
16 | @property | 16 | @property | ||
17 | def mass(self): | 17 | def mass(self): | ||
18 | return self._mass | 18 | return self._mass | ||
19 | 19 | ||||
20 | @property | 20 | @property | ||
21 | def volume(self): | 21 | def volume(self): | ||
22 | """Calculate the volume of the material based on its density.""" | 22 | """Calculate the volume of the material based on its density.""" | ||
23 | return self.mass / self.density | 23 | return self.mass / self.density | ||
24 | 24 | ||||
25 | 25 | ||||
26 | class Concrete(BuildingMaterial): | 26 | class Concrete(BuildingMaterial): | ||
27 | """Represents concrete with a specific density.""" | 27 | """Represents concrete with a specific density.""" | ||
28 | 28 | ||||
29 | density = 2500 | 29 | density = 2500 | ||
30 | 30 | ||||
31 | 31 | ||||
32 | class Brick(BuildingMaterial): | 32 | class Brick(BuildingMaterial): | ||
33 | """Represents brick with a specific density.""" | 33 | """Represents brick with a specific density.""" | ||
34 | 34 | ||||
35 | density = 2000 | 35 | density = 2000 | ||
36 | 36 | ||||
37 | 37 | ||||
38 | class Stone(BuildingMaterial): | 38 | class Stone(BuildingMaterial): | ||
39 | """Represents stone with a specific density.""" | 39 | """Represents stone with a specific density.""" | ||
40 | 40 | ||||
41 | density = 1600 | 41 | density = 1600 | ||
42 | 42 | ||||
43 | 43 | ||||
44 | class Wood(BuildingMaterial): | 44 | class Wood(BuildingMaterial): | ||
45 | """Represents wood with a specific density.""" | 45 | """Represents wood with a specific density.""" | ||
46 | 46 | ||||
47 | density = 600 | 47 | density = 600 | ||
48 | 48 | ||||
49 | 49 | ||||
50 | class Steel(BuildingMaterial): | 50 | class Steel(BuildingMaterial): | ||
51 | """Represents steel with a specific density.""" | 51 | """Represents steel with a specific density.""" | ||
52 | 52 | ||||
53 | density = 7700 | 53 | density = 7700 | ||
54 | 54 | ||||
55 | 55 | ||||
56 | class Factory: | 56 | class Factory: | ||
57 | """Represents a factory that produces building materials and alloys.""" | 57 | """Represents a factory that produces building materials and alloys.""" | ||
58 | 58 | ||||
59 | _VALID_MATERIALS = { | 59 | _VALID_MATERIALS = { | ||
60 | "Concrete": Concrete, | 60 | "Concrete": Concrete, | ||
61 | "Brick": Brick, | 61 | "Brick": Brick, | ||
62 | "Stone": Stone, | 62 | "Stone": Stone, | ||
63 | "Wood": Wood, | 63 | "Wood": Wood, | ||
64 | "Steel": Steel | 64 | "Steel": Steel | ||
65 | } | 65 | } | ||
66 | 66 | ||||
67 | _created_alloy_types = {} | 67 | _created_alloy_types = {} | ||
68 | _total_volume_sum = 0 | 68 | _total_volume_sum = 0 | ||
69 | 69 | ||||
70 | def __init__(self): | 70 | def __init__(self): | ||
71 | """Initialize a Factory instance.""" | 71 | """Initialize a Factory instance.""" | ||
72 | self._instance_volume_sum = 0 | 72 | self._instance_volume_sum = 0 | ||
73 | 73 | ||||
74 | def _decrease_volume_sums(self, vol): | 74 | def _decrease_volume_sums(self, vol): | ||
75 | self._instance_volume_sum -= vol | 75 | self._instance_volume_sum -= vol | ||
76 | Factory._total_volume_sum -= vol | 76 | Factory._total_volume_sum -= vol | ||
77 | 77 | ||||
78 | def _increase_volume_sums(self, vol): | 78 | def _increase_volume_sums(self, vol): | ||
79 | self._instance_volume_sum += vol | 79 | self._instance_volume_sum += vol | ||
80 | Factory._total_volume_sum += vol | 80 | Factory._total_volume_sum += vol | ||
81 | 81 | ||||
82 | def _create_single_material(self, material_name, mass): | 82 | def _create_single_material(self, material_name, mass): | ||
83 | if material_name in Factory._VALID_MATERIALS: | 83 | if material_name in Factory._VALID_MATERIALS: | ||
84 | return Factory._VALID_MATERIALS[material_name](mass, self) | 84 | return Factory._VALID_MATERIALS[material_name](mass, self) | ||
85 | elif material_name in Factory._created_alloy_types: | 85 | elif material_name in Factory._created_alloy_types: | ||
86 | return Factory._created_alloy_types[material_name](mass, self) | 86 | return Factory._created_alloy_types[material_name](mass, self) | ||
87 | else: | 87 | else: | ||
88 | raise ValueError("Keyword arguments must have valid materials as keys!") | 88 | raise ValueError("Keyword arguments must have valid materials as keys!") | ||
89 | 89 | ||||
90 | def _create_materials(self, **kwargs): | 90 | def _create_materials(self, **kwargs): | ||
91 | result = [self._create_single_material(key, value) for key, value in kwargs.items()] | 91 | result = [self._create_single_material(key, value) for key, value in kwargs.items()] | ||
n | 92 | self._increase_volume_sums(sum([m.volume for m in result])) | n | 92 | self._increase_volume_sums(sum(m.volume for m in result)) |
93 | return tuple(result) | 93 | return tuple(result) | ||
94 | 94 | ||||
95 | @staticmethod | 95 | @staticmethod | ||
96 | def _generate_alloy_name(materials): | 96 | def _generate_alloy_name(materials): | ||
97 | names = [] | 97 | names = [] | ||
98 | for material in set(materials): | 98 | for material in set(materials): | ||
99 | names.extend(material.__class__.__name__.split("_")) | 99 | names.extend(material.__class__.__name__.split("_")) | ||
100 | return "_".join(sorted(names)) | 100 | return "_".join(sorted(names)) | ||
101 | 101 | ||||
102 | @classmethod | 102 | @classmethod | ||
103 | def _add_new_alloy_type(cls, alloy_name): | 103 | def _add_new_alloy_type(cls, alloy_name): | ||
104 | material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")] | 104 | material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")] | ||
105 | avg_density = sum(m.density for m in material_types) / len(material_types) | 105 | avg_density = sum(m.density for m in material_types) / len(material_types) | ||
n | 106 | alloy_type = type( | n | 106 | cls._created_alloy_types[alloy_name] = type( |
107 | alloy_name, | 107 | alloy_name, | ||
108 | (BuildingMaterial,), | 108 | (BuildingMaterial,), | ||
109 | { | 109 | { | ||
n | 110 | "__init__": lambda self, mass, factory: super(alloy_type, self).__init__(mass, factory), | n | ||
111 | "density": avg_density, | 110 | "density": avg_density, | ||
112 | } | 111 | } | ||
113 | ) | 112 | ) | ||
n | 114 | cls._created_alloy_types[alloy_name] = alloy_type | n | ||
115 | 113 | ||||
116 | @staticmethod | 114 | @staticmethod | ||
117 | def _invalidate_materials(materials): | 115 | def _invalidate_materials(materials): | ||
118 | for m in materials: | 116 | for m in materials: | ||
119 | m._used = True | 117 | m._used = True | ||
t | 120 | if m._factory is not None: | t | 118 | if m.factory: |
121 | m._factory._decrease_volume_sums(m.volume) | 119 | m.factory._decrease_volume_sums(m.volume) | ||
122 | m._factory = None | 120 | m.factory = None | ||
123 | 121 | ||||
124 | def _create_dynamic_alloy(self, *materials): | 122 | def _create_dynamic_alloy(self, *materials): | ||
125 | for m in materials: | 123 | for m in materials: | ||
126 | if m._used: | 124 | if m._used: | ||
127 | raise AssertionError("BuildingMaterial objects can't be reused!") | 125 | raise AssertionError("BuildingMaterial objects can't be reused!") | ||
128 | 126 | ||||
129 | alloy_name = Factory._generate_alloy_name(materials) | 127 | alloy_name = Factory._generate_alloy_name(materials) | ||
130 | mass_sum = sum(m.mass for m in materials) | 128 | mass_sum = sum(m.mass for m in materials) | ||
131 | 129 | ||||
132 | if alloy_name not in Factory._created_alloy_types: | 130 | if alloy_name not in Factory._created_alloy_types: | ||
133 | Factory._add_new_alloy_type(alloy_name) | 131 | Factory._add_new_alloy_type(alloy_name) | ||
134 | 132 | ||||
135 | Factory._invalidate_materials(materials) | 133 | Factory._invalidate_materials(materials) | ||
136 | 134 | ||||
137 | alloy = Factory._created_alloy_types[alloy_name](mass_sum, self) | 135 | alloy = Factory._created_alloy_types[alloy_name](mass_sum, self) | ||
138 | self._increase_volume_sums(alloy.volume) | 136 | self._increase_volume_sums(alloy.volume) | ||
139 | return alloy | 137 | return alloy | ||
140 | 138 | ||||
141 | @staticmethod | 139 | @staticmethod | ||
142 | def _validate_call_arguments(*args, **kwargs): | 140 | def _validate_call_arguments(*args, **kwargs): | ||
143 | if args and kwargs: | 141 | if args and kwargs: | ||
144 | raise ValueError("Factory can't be called with both positional and keyword arguments!") | 142 | raise ValueError("Factory can't be called with both positional and keyword arguments!") | ||
145 | if not args and not kwargs: | 143 | if not args and not kwargs: | ||
146 | raise ValueError("Factory object can't be called without arguments!") | 144 | raise ValueError("Factory object can't be called without arguments!") | ||
147 | 145 | ||||
148 | def __call__(self, *args, **kwargs): | 146 | def __call__(self, *args, **kwargs): | ||
149 | """ | 147 | """ | ||
150 | Create materials or an alloy using the factory. | 148 | Create materials or an alloy using the factory. | ||
151 | 149 | ||||
152 | Args: | 150 | Args: | ||
153 | *args: Positional arguments for dynamic alloys. | 151 | *args: Positional arguments for dynamic alloys. | ||
154 | **kwargs: Keyword arguments for creating materials. | 152 | **kwargs: Keyword arguments for creating materials. | ||
155 | """ | 153 | """ | ||
156 | Factory._validate_call_arguments(*args, **kwargs) | 154 | Factory._validate_call_arguments(*args, **kwargs) | ||
157 | if kwargs: | 155 | if kwargs: | ||
158 | return self._create_materials(**kwargs) | 156 | return self._create_materials(**kwargs) | ||
159 | elif args: | 157 | elif args: | ||
160 | alloy = self._create_dynamic_alloy(*args) | 158 | alloy = self._create_dynamic_alloy(*args) | ||
161 | return alloy | 159 | return alloy | ||
162 | 160 | ||||
163 | def can_build(self, volume_target): | 161 | def can_build(self, volume_target): | ||
164 | """Check if the factory can build with the specified volume.""" | 162 | """Check if the factory can build with the specified volume.""" | ||
165 | return self._instance_volume_sum >= volume_target | 163 | return self._instance_volume_sum >= volume_target | ||
166 | 164 | ||||
167 | @classmethod | 165 | @classmethod | ||
168 | def can_build_together(cls, volume_target): | 166 | def can_build_together(cls, volume_target): | ||
169 | """Check if all factories combined can build with the specified volume.""" | 167 | """Check if all factories combined can build with the specified volume.""" | ||
170 | return cls._total_volume_sum >= volume_target | 168 | return cls._total_volume_sum >= volume_target |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|