1class Material:
2 def __init__(self, mass, density, materials_count = 1, factory = None):
3 self.mass = mass
4 self.density = density
5 self.materials_count = materials_count
6 self.is_valid = True
7 self.factory = factory
8
9 @property
10 def volume(self):
11 return float(self.mass / self.density)
12
13
14class Concrete(Material):
15 def __init__(self, mass):
16 super().__init__(mass, density=2500)
17
18
19class Brick(Material):
20 def __init__(self, mass):
21 super().__init__(mass, density=2000)
22
23
24class Stone(Material):
25 def __init__(self, mass):
26 super().__init__(mass, density=1600)
27
28
29class Wood(Material):
30 def __init__(self, mass):
31 super().__init__(mass, density=600)
32
33
34class Steel(Material):
35 def __init__(self, mass):
36 super().__init__(mass, density=7700)
37
38class Factory:
39 MATERIALS = {
40 "Concrete": Concrete,
41 "Brick": Brick,
42 "Stone": Stone,
43 "Wood": Wood,
44 "Steel": Steel
45 }
46
47 global_classes = {}
48 all_created_materials = []
49
50 def __init__(self):
51 self.classes = {}
52 self.created_materials = []
53
54 def __call__(self, *args, **kwargs):
55 if args and kwargs:
56 raise ValueError("Unvalid calling of Factory with args and kwargs")
57
58 if not args and not kwargs:
59 raise ValueError("Unvalid calling of factory without any params")
60
61 if args:
62 if not all(isinstance(arg, Material) for arg in args):
63 raise ValueError("All positional arguments must be instances of Material.")
64 if not all(arg.is_valid for arg in args):
65 raise AssertionError("All positional arguments must be valid materials.")
66 # Generate class name
67 material_names = sorted(arg.__class__.__name__ for arg in args)
68 new_material_class_name = "_".join(material_names)
69 new_materials_count = 0
70 # If not already existing - calc avrg density and create new class
71 if new_material_class_name not in Factory.global_classes:
72 new_materials_count = sum(arg.materials_count for arg in args)
73 total_density = sum(arg.density * arg.materials_count for arg in args)
74 average_density = total_density / new_materials_count
75
76 class NewMaterial(Material):
77 def __init__(self, mass):
78 super().__init__(mass=mass, density=average_density, materials_count=new_materials_count)
79
80 NewMaterial.__name__ = new_material_class_name
81 Factory.global_classes[new_material_class_name] = NewMaterial
82 # Mark the materials as already used and return instance of the new class
83 for arg in args:
84 arg.is_valid = False
85 total_mass = sum(arg.mass for arg in args)
86 new_instance = Factory.global_classes[new_material_class_name](total_mass)
87 new_instance.factory = self
88 self.created_materials.append(new_instance)
89 Factory.all_created_materials.append(new_instance)
90 return new_instance
91
92 if kwargs:
93 instances = []
94 for key, value in kwargs.items():
95 if key not in self.MATERIALS and key not in Factory.global_classes:
96 raise ValueError("Invalid material name.")
97 material_class = self.MATERIALS.get(key) or Factory.global_classes[key]
98 instance = material_class(value)
99 instance.factory = self
100 instances.append(instance)
101 self.created_materials.append(instance)
102 Factory.all_created_materials.append(instance)
103 return tuple(instances)
104
105 def can_build(self, required_volume):
106 total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid)
107 return total_volume >= required_volume
108
109 @classmethod
110 def can_build_together(cls, required_volume):
111 total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid)
112 return total_volume >= required_volume
......FF..
======================================================================
FAIL: test_positional_arguments_multiple_argument_with_dynamics (test.TestFactory.test_positional_arguments_multiple_argument_with_dynamics)
Test calling a factory using multiple positional arguments.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 128, in test_positional_arguments_multiple_argument_with_dynamics
self.assertEqual(mega_material.__class__.__name__,
AssertionError: 'Brick_Concrete_Stone_Steel_Wood' != 'Brick_Concrete_Steel_Stone_Wood'
- Brick_Concrete_Stone_Steel_Wood
? ------
+ Brick_Concrete_Steel_Stone_Wood
? ++++++
======================================================================
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.Factory.__call__.<locals>.NewMaterial'> is not <class 'solution.Concrete'>
----------------------------------------------------------------------
Ran 10 tests in 0.013s
FAILED (failures=2)
f | 1 | class Material: | f | 1 | class Material: |
2 | def __init__(self, mass, density, materials_count = 1, factory = None): | 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | ||
3 | self.mass = mass | 3 | self.mass = mass | ||
4 | self.density = density | 4 | self.density = density | ||
5 | self.materials_count = materials_count | 5 | self.materials_count = materials_count | ||
6 | self.is_valid = True | 6 | self.is_valid = True | ||
n | 7 | self.factory = factory | n | 7 | self.factory = factory |
8 | 8 | ||||
9 | @property | 9 | @property | ||
10 | def volume(self): | 10 | def volume(self): | ||
11 | return float(self.mass / self.density) | 11 | return float(self.mass / self.density) | ||
12 | 12 | ||||
13 | 13 | ||||
14 | class Concrete(Material): | 14 | class Concrete(Material): | ||
15 | def __init__(self, mass): | 15 | def __init__(self, mass): | ||
16 | super().__init__(mass, density=2500) | 16 | super().__init__(mass, density=2500) | ||
17 | 17 | ||||
18 | 18 | ||||
19 | class Brick(Material): | 19 | class Brick(Material): | ||
20 | def __init__(self, mass): | 20 | def __init__(self, mass): | ||
21 | super().__init__(mass, density=2000) | 21 | super().__init__(mass, density=2000) | ||
22 | 22 | ||||
23 | 23 | ||||
24 | class Stone(Material): | 24 | class Stone(Material): | ||
25 | def __init__(self, mass): | 25 | def __init__(self, mass): | ||
26 | super().__init__(mass, density=1600) | 26 | super().__init__(mass, density=1600) | ||
27 | 27 | ||||
28 | 28 | ||||
29 | class Wood(Material): | 29 | class Wood(Material): | ||
30 | def __init__(self, mass): | 30 | def __init__(self, mass): | ||
31 | super().__init__(mass, density=600) | 31 | super().__init__(mass, density=600) | ||
32 | 32 | ||||
33 | 33 | ||||
34 | class Steel(Material): | 34 | class Steel(Material): | ||
35 | def __init__(self, mass): | 35 | def __init__(self, mass): | ||
36 | super().__init__(mass, density=7700) | 36 | super().__init__(mass, density=7700) | ||
37 | 37 | ||||
38 | class Factory: | 38 | class Factory: | ||
39 | MATERIALS = { | 39 | MATERIALS = { | ||
40 | "Concrete": Concrete, | 40 | "Concrete": Concrete, | ||
41 | "Brick": Brick, | 41 | "Brick": Brick, | ||
42 | "Stone": Stone, | 42 | "Stone": Stone, | ||
43 | "Wood": Wood, | 43 | "Wood": Wood, | ||
44 | "Steel": Steel | 44 | "Steel": Steel | ||
45 | } | 45 | } | ||
46 | 46 | ||||
47 | global_classes = {} | 47 | global_classes = {} | ||
48 | all_created_materials = [] | 48 | all_created_materials = [] | ||
49 | 49 | ||||
50 | def __init__(self): | 50 | def __init__(self): | ||
51 | self.classes = {} | 51 | self.classes = {} | ||
52 | self.created_materials = [] | 52 | self.created_materials = [] | ||
53 | 53 | ||||
54 | def __call__(self, *args, **kwargs): | 54 | def __call__(self, *args, **kwargs): | ||
55 | if args and kwargs: | 55 | if args and kwargs: | ||
56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | ||
57 | 57 | ||||
58 | if not args and not kwargs: | 58 | if not args and not kwargs: | ||
59 | raise ValueError("Unvalid calling of factory without any params") | 59 | raise ValueError("Unvalid calling of factory without any params") | ||
60 | 60 | ||||
61 | if args: | 61 | if args: | ||
62 | if not all(isinstance(arg, Material) for arg in args): | 62 | if not all(isinstance(arg, Material) for arg in args): | ||
63 | raise ValueError("All positional arguments must be instances of Material.") | 63 | raise ValueError("All positional arguments must be instances of Material.") | ||
64 | if not all(arg.is_valid for arg in args): | 64 | if not all(arg.is_valid for arg in args): | ||
t | 65 | raise AssertionError("All positional arguments must be valid materials.") | t | 65 | raise AssertionError("All positional arguments must be valid materials.") |
66 | if not all(arg.factory is self for arg in args): | ||||
67 | raise AssertionError("Materials can only be reused in the factory that created them.") | ||||
68 | # Generate class name | 66 | # Generate class name | ||
69 | material_names = sorted(arg.__class__.__name__ for arg in args) | 67 | material_names = sorted(arg.__class__.__name__ for arg in args) | ||
70 | new_material_class_name = "_".join(material_names) | 68 | new_material_class_name = "_".join(material_names) | ||
71 | new_materials_count = 0 | 69 | new_materials_count = 0 | ||
72 | # If not already existing - calc avrg density and create new class | 70 | # If not already existing - calc avrg density and create new class | ||
73 | if new_material_class_name not in Factory.global_classes: | 71 | if new_material_class_name not in Factory.global_classes: | ||
74 | new_materials_count = sum(arg.materials_count for arg in args) | 72 | new_materials_count = sum(arg.materials_count for arg in args) | ||
75 | total_density = sum(arg.density * arg.materials_count for arg in args) | 73 | total_density = sum(arg.density * arg.materials_count for arg in args) | ||
76 | average_density = total_density / new_materials_count | 74 | average_density = total_density / new_materials_count | ||
77 | 75 | ||||
78 | class NewMaterial(Material): | 76 | class NewMaterial(Material): | ||
79 | def __init__(self, mass): | 77 | def __init__(self, mass): | ||
80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | 78 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | ||
81 | 79 | ||||
82 | NewMaterial.__name__ = new_material_class_name | 80 | NewMaterial.__name__ = new_material_class_name | ||
83 | Factory.global_classes[new_material_class_name] = NewMaterial | 81 | Factory.global_classes[new_material_class_name] = NewMaterial | ||
84 | # Mark the materials as already used and return instance of the new class | 82 | # Mark the materials as already used and return instance of the new class | ||
85 | for arg in args: | 83 | for arg in args: | ||
86 | arg.is_valid = False | 84 | arg.is_valid = False | ||
87 | total_mass = sum(arg.mass for arg in args) | 85 | total_mass = sum(arg.mass for arg in args) | ||
88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | 86 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | ||
89 | new_instance.factory = self | 87 | new_instance.factory = self | ||
90 | self.created_materials.append(new_instance) | 88 | self.created_materials.append(new_instance) | ||
91 | Factory.all_created_materials.append(new_instance) | 89 | Factory.all_created_materials.append(new_instance) | ||
92 | return new_instance | 90 | return new_instance | ||
93 | 91 | ||||
94 | if kwargs: | 92 | if kwargs: | ||
95 | instances = [] | 93 | instances = [] | ||
96 | for key, value in kwargs.items(): | 94 | for key, value in kwargs.items(): | ||
97 | if key not in self.MATERIALS and key not in Factory.global_classes: | 95 | if key not in self.MATERIALS and key not in Factory.global_classes: | ||
98 | raise ValueError("Invalid material name.") | 96 | raise ValueError("Invalid material name.") | ||
99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | 97 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | ||
100 | instance = material_class(value) | 98 | instance = material_class(value) | ||
101 | instance.factory = self | 99 | instance.factory = self | ||
102 | instances.append(instance) | 100 | instances.append(instance) | ||
103 | self.created_materials.append(instance) | 101 | self.created_materials.append(instance) | ||
104 | Factory.all_created_materials.append(instance) | 102 | Factory.all_created_materials.append(instance) | ||
105 | return tuple(instances) | 103 | return tuple(instances) | ||
106 | 104 | ||||
107 | def can_build(self, required_volume): | 105 | def can_build(self, required_volume): | ||
108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | 106 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | ||
109 | return total_volume >= required_volume | 107 | return total_volume >= required_volume | ||
110 | 108 | ||||
111 | @classmethod | 109 | @classmethod | ||
112 | def can_build_together(cls, required_volume): | 110 | def can_build_together(cls, required_volume): | ||
113 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | 111 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | ||
114 | return total_volume >= required_volume | 112 | return total_volume >= required_volume |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Material: | f | 1 | class Material: |
2 | def __init__(self, mass, density, materials_count = 1, factory = None): | 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | ||
3 | self.mass = mass | 3 | self.mass = mass | ||
4 | self.density = density | 4 | self.density = density | ||
5 | self.materials_count = materials_count | 5 | self.materials_count = materials_count | ||
6 | self.is_valid = True | 6 | self.is_valid = True | ||
7 | self.factory = factory | 7 | self.factory = factory | ||
8 | 8 | ||||
9 | @property | 9 | @property | ||
10 | def volume(self): | 10 | def volume(self): | ||
11 | return float(self.mass / self.density) | 11 | return float(self.mass / self.density) | ||
12 | 12 | ||||
13 | 13 | ||||
14 | class Concrete(Material): | 14 | class Concrete(Material): | ||
15 | def __init__(self, mass): | 15 | def __init__(self, mass): | ||
16 | super().__init__(mass, density=2500) | 16 | super().__init__(mass, density=2500) | ||
17 | 17 | ||||
18 | 18 | ||||
19 | class Brick(Material): | 19 | class Brick(Material): | ||
20 | def __init__(self, mass): | 20 | def __init__(self, mass): | ||
21 | super().__init__(mass, density=2000) | 21 | super().__init__(mass, density=2000) | ||
22 | 22 | ||||
23 | 23 | ||||
24 | class Stone(Material): | 24 | class Stone(Material): | ||
25 | def __init__(self, mass): | 25 | def __init__(self, mass): | ||
26 | super().__init__(mass, density=1600) | 26 | super().__init__(mass, density=1600) | ||
27 | 27 | ||||
28 | 28 | ||||
29 | class Wood(Material): | 29 | class Wood(Material): | ||
30 | def __init__(self, mass): | 30 | def __init__(self, mass): | ||
31 | super().__init__(mass, density=600) | 31 | super().__init__(mass, density=600) | ||
32 | 32 | ||||
33 | 33 | ||||
34 | class Steel(Material): | 34 | class Steel(Material): | ||
35 | def __init__(self, mass): | 35 | def __init__(self, mass): | ||
36 | super().__init__(mass, density=7700) | 36 | super().__init__(mass, density=7700) | ||
37 | 37 | ||||
38 | class Factory: | 38 | class Factory: | ||
39 | MATERIALS = { | 39 | MATERIALS = { | ||
40 | "Concrete": Concrete, | 40 | "Concrete": Concrete, | ||
41 | "Brick": Brick, | 41 | "Brick": Brick, | ||
42 | "Stone": Stone, | 42 | "Stone": Stone, | ||
43 | "Wood": Wood, | 43 | "Wood": Wood, | ||
44 | "Steel": Steel | 44 | "Steel": Steel | ||
45 | } | 45 | } | ||
46 | 46 | ||||
47 | global_classes = {} | 47 | global_classes = {} | ||
48 | all_created_materials = [] | 48 | all_created_materials = [] | ||
49 | 49 | ||||
50 | def __init__(self): | 50 | def __init__(self): | ||
51 | self.classes = {} | 51 | self.classes = {} | ||
52 | self.created_materials = [] | 52 | self.created_materials = [] | ||
53 | 53 | ||||
54 | def __call__(self, *args, **kwargs): | 54 | def __call__(self, *args, **kwargs): | ||
55 | if args and kwargs: | 55 | if args and kwargs: | ||
56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | ||
57 | 57 | ||||
58 | if not args and not kwargs: | 58 | if not args and not kwargs: | ||
59 | raise ValueError("Unvalid calling of factory without any params") | 59 | raise ValueError("Unvalid calling of factory without any params") | ||
60 | 60 | ||||
61 | if args: | 61 | if args: | ||
62 | if not all(isinstance(arg, Material) for arg in args): | 62 | if not all(isinstance(arg, Material) for arg in args): | ||
63 | raise ValueError("All positional arguments must be instances of Material.") | 63 | raise ValueError("All positional arguments must be instances of Material.") | ||
64 | if not all(arg.is_valid for arg in args): | 64 | if not all(arg.is_valid for arg in args): | ||
65 | raise AssertionError("All positional arguments must be valid materials.") | 65 | raise AssertionError("All positional arguments must be valid materials.") | ||
66 | if not all(arg.factory is self for arg in args): | 66 | if not all(arg.factory is self for arg in args): | ||
67 | raise AssertionError("Materials can only be reused in the factory that created them.") | 67 | raise AssertionError("Materials can only be reused in the factory that created them.") | ||
68 | # Generate class name | 68 | # Generate class name | ||
69 | material_names = sorted(arg.__class__.__name__ for arg in args) | 69 | material_names = sorted(arg.__class__.__name__ for arg in args) | ||
70 | new_material_class_name = "_".join(material_names) | 70 | new_material_class_name = "_".join(material_names) | ||
71 | new_materials_count = 0 | 71 | new_materials_count = 0 | ||
72 | # If not already existing - calc avrg density and create new class | 72 | # If not already existing - calc avrg density and create new class | ||
73 | if new_material_class_name not in Factory.global_classes: | 73 | if new_material_class_name not in Factory.global_classes: | ||
74 | new_materials_count = sum(arg.materials_count for arg in args) | 74 | new_materials_count = sum(arg.materials_count for arg in args) | ||
75 | total_density = sum(arg.density * arg.materials_count for arg in args) | 75 | total_density = sum(arg.density * arg.materials_count for arg in args) | ||
76 | average_density = total_density / new_materials_count | 76 | average_density = total_density / new_materials_count | ||
77 | 77 | ||||
78 | class NewMaterial(Material): | 78 | class NewMaterial(Material): | ||
79 | def __init__(self, mass): | 79 | def __init__(self, mass): | ||
80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | 80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | ||
81 | 81 | ||||
82 | NewMaterial.__name__ = new_material_class_name | 82 | NewMaterial.__name__ = new_material_class_name | ||
83 | Factory.global_classes[new_material_class_name] = NewMaterial | 83 | Factory.global_classes[new_material_class_name] = NewMaterial | ||
84 | # Mark the materials as already used and return instance of the new class | 84 | # Mark the materials as already used and return instance of the new class | ||
85 | for arg in args: | 85 | for arg in args: | ||
86 | arg.is_valid = False | 86 | arg.is_valid = False | ||
87 | total_mass = sum(arg.mass for arg in args) | 87 | total_mass = sum(arg.mass for arg in args) | ||
88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | 88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | ||
89 | new_instance.factory = self | 89 | new_instance.factory = self | ||
90 | self.created_materials.append(new_instance) | 90 | self.created_materials.append(new_instance) | ||
91 | Factory.all_created_materials.append(new_instance) | 91 | Factory.all_created_materials.append(new_instance) | ||
92 | return new_instance | 92 | return new_instance | ||
93 | 93 | ||||
94 | if kwargs: | 94 | if kwargs: | ||
95 | instances = [] | 95 | instances = [] | ||
96 | for key, value in kwargs.items(): | 96 | for key, value in kwargs.items(): | ||
97 | if key not in self.MATERIALS and key not in Factory.global_classes: | 97 | if key not in self.MATERIALS and key not in Factory.global_classes: | ||
98 | raise ValueError("Invalid material name.") | 98 | raise ValueError("Invalid material name.") | ||
99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | 99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | ||
100 | instance = material_class(value) | 100 | instance = material_class(value) | ||
101 | instance.factory = self | 101 | instance.factory = self | ||
102 | instances.append(instance) | 102 | instances.append(instance) | ||
103 | self.created_materials.append(instance) | 103 | self.created_materials.append(instance) | ||
104 | Factory.all_created_materials.append(instance) | 104 | Factory.all_created_materials.append(instance) | ||
105 | return tuple(instances) | 105 | return tuple(instances) | ||
106 | 106 | ||||
107 | def can_build(self, required_volume): | 107 | def can_build(self, required_volume): | ||
108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | 108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | ||
109 | return total_volume >= required_volume | 109 | return total_volume >= required_volume | ||
t | 110 | t | |||
111 | 110 | ||||
112 | @classmethod | 111 | @classmethod | ||
113 | def can_build_together(cls, required_volume): | 112 | def can_build_together(cls, required_volume): | ||
114 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | 113 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | ||
115 | return total_volume >= required_volume | 114 | return total_volume >= required_volume |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
t | 1 | class Material: | t | 1 | class Material: |
2 | def __init__(self, mass, density, materials_count = 1, factory = None): | 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | ||
3 | self.mass = mass | 3 | self.mass = mass | ||
4 | self.density = density | 4 | self.density = density | ||
5 | self.materials_count = materials_count | 5 | self.materials_count = materials_count | ||
6 | self.is_valid = True | 6 | self.is_valid = True | ||
7 | self.factory = factory | 7 | self.factory = factory | ||
8 | 8 | ||||
9 | @property | 9 | @property | ||
10 | def volume(self): | 10 | def volume(self): | ||
11 | return float(self.mass / self.density) | 11 | return float(self.mass / self.density) | ||
12 | 12 | ||||
13 | 13 | ||||
14 | class Concrete(Material): | 14 | class Concrete(Material): | ||
15 | def __init__(self, mass): | 15 | def __init__(self, mass): | ||
16 | super().__init__(mass, density=2500) | 16 | super().__init__(mass, density=2500) | ||
17 | 17 | ||||
18 | 18 | ||||
19 | class Brick(Material): | 19 | class Brick(Material): | ||
20 | def __init__(self, mass): | 20 | def __init__(self, mass): | ||
21 | super().__init__(mass, density=2000) | 21 | super().__init__(mass, density=2000) | ||
22 | 22 | ||||
23 | 23 | ||||
24 | class Stone(Material): | 24 | class Stone(Material): | ||
25 | def __init__(self, mass): | 25 | def __init__(self, mass): | ||
26 | super().__init__(mass, density=1600) | 26 | super().__init__(mass, density=1600) | ||
27 | 27 | ||||
28 | 28 | ||||
29 | class Wood(Material): | 29 | class Wood(Material): | ||
30 | def __init__(self, mass): | 30 | def __init__(self, mass): | ||
31 | super().__init__(mass, density=600) | 31 | super().__init__(mass, density=600) | ||
32 | 32 | ||||
33 | 33 | ||||
34 | class Steel(Material): | 34 | class Steel(Material): | ||
35 | def __init__(self, mass): | 35 | def __init__(self, mass): | ||
36 | super().__init__(mass, density=7700) | 36 | super().__init__(mass, density=7700) | ||
37 | 37 | ||||
38 | class Factory: | 38 | class Factory: | ||
39 | MATERIALS = { | 39 | MATERIALS = { | ||
40 | "Concrete": Concrete, | 40 | "Concrete": Concrete, | ||
41 | "Brick": Brick, | 41 | "Brick": Brick, | ||
42 | "Stone": Stone, | 42 | "Stone": Stone, | ||
43 | "Wood": Wood, | 43 | "Wood": Wood, | ||
44 | "Steel": Steel | 44 | "Steel": Steel | ||
45 | } | 45 | } | ||
46 | 46 | ||||
47 | global_classes = {} | 47 | global_classes = {} | ||
48 | all_created_materials = [] | 48 | all_created_materials = [] | ||
49 | 49 | ||||
50 | def __init__(self): | 50 | def __init__(self): | ||
51 | self.classes = {} | 51 | self.classes = {} | ||
52 | self.created_materials = [] | 52 | self.created_materials = [] | ||
53 | 53 | ||||
54 | def __call__(self, *args, **kwargs): | 54 | def __call__(self, *args, **kwargs): | ||
55 | if args and kwargs: | 55 | if args and kwargs: | ||
56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | ||
57 | 57 | ||||
58 | if not args and not kwargs: | 58 | if not args and not kwargs: | ||
59 | raise ValueError("Unvalid calling of factory without any params") | 59 | raise ValueError("Unvalid calling of factory without any params") | ||
60 | 60 | ||||
61 | if args: | 61 | if args: | ||
62 | if not all(isinstance(arg, Material) for arg in args): | 62 | if not all(isinstance(arg, Material) for arg in args): | ||
63 | raise ValueError("All positional arguments must be instances of Material.") | 63 | raise ValueError("All positional arguments must be instances of Material.") | ||
64 | if not all(arg.is_valid for arg in args): | 64 | if not all(arg.is_valid for arg in args): | ||
65 | raise AssertionError("All positional arguments must be valid materials.") | 65 | raise AssertionError("All positional arguments must be valid materials.") | ||
66 | if not all(arg.factory is self for arg in args): | 66 | if not all(arg.factory is self for arg in args): | ||
67 | raise AssertionError("Materials can only be reused in the factory that created them.") | 67 | raise AssertionError("Materials can only be reused in the factory that created them.") | ||
68 | # Generate class name | 68 | # Generate class name | ||
69 | material_names = sorted(arg.__class__.__name__ for arg in args) | 69 | material_names = sorted(arg.__class__.__name__ for arg in args) | ||
70 | new_material_class_name = "_".join(material_names) | 70 | new_material_class_name = "_".join(material_names) | ||
71 | new_materials_count = 0 | 71 | new_materials_count = 0 | ||
72 | # If not already existing - calc avrg density and create new class | 72 | # If not already existing - calc avrg density and create new class | ||
73 | if new_material_class_name not in Factory.global_classes: | 73 | if new_material_class_name not in Factory.global_classes: | ||
74 | new_materials_count = sum(arg.materials_count for arg in args) | 74 | new_materials_count = sum(arg.materials_count for arg in args) | ||
75 | total_density = sum(arg.density * arg.materials_count for arg in args) | 75 | total_density = sum(arg.density * arg.materials_count for arg in args) | ||
76 | average_density = total_density / new_materials_count | 76 | average_density = total_density / new_materials_count | ||
77 | 77 | ||||
78 | class NewMaterial(Material): | 78 | class NewMaterial(Material): | ||
79 | def __init__(self, mass): | 79 | def __init__(self, mass): | ||
80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | 80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | ||
81 | 81 | ||||
82 | NewMaterial.__name__ = new_material_class_name | 82 | NewMaterial.__name__ = new_material_class_name | ||
83 | Factory.global_classes[new_material_class_name] = NewMaterial | 83 | Factory.global_classes[new_material_class_name] = NewMaterial | ||
84 | # Mark the materials as already used and return instance of the new class | 84 | # Mark the materials as already used and return instance of the new class | ||
85 | for arg in args: | 85 | for arg in args: | ||
86 | arg.is_valid = False | 86 | arg.is_valid = False | ||
87 | total_mass = sum(arg.mass for arg in args) | 87 | total_mass = sum(arg.mass for arg in args) | ||
88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | 88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | ||
89 | new_instance.factory = self | 89 | new_instance.factory = self | ||
90 | self.created_materials.append(new_instance) | 90 | self.created_materials.append(new_instance) | ||
91 | Factory.all_created_materials.append(new_instance) | 91 | Factory.all_created_materials.append(new_instance) | ||
92 | return new_instance | 92 | return new_instance | ||
93 | 93 | ||||
94 | if kwargs: | 94 | if kwargs: | ||
95 | instances = [] | 95 | instances = [] | ||
96 | for key, value in kwargs.items(): | 96 | for key, value in kwargs.items(): | ||
97 | if key not in self.MATERIALS and key not in Factory.global_classes: | 97 | if key not in self.MATERIALS and key not in Factory.global_classes: | ||
98 | raise ValueError("Invalid material name.") | 98 | raise ValueError("Invalid material name.") | ||
99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | 99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | ||
100 | instance = material_class(value) | 100 | instance = material_class(value) | ||
101 | instance.factory = self | 101 | instance.factory = self | ||
102 | instances.append(instance) | 102 | instances.append(instance) | ||
103 | self.created_materials.append(instance) | 103 | self.created_materials.append(instance) | ||
104 | Factory.all_created_materials.append(instance) | 104 | Factory.all_created_materials.append(instance) | ||
105 | return tuple(instances) | 105 | return tuple(instances) | ||
106 | 106 | ||||
107 | def can_build(self, required_volume): | 107 | def can_build(self, required_volume): | ||
108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | 108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | ||
109 | return total_volume >= required_volume | 109 | return total_volume >= required_volume | ||
110 | 110 | ||||
111 | 111 | ||||
112 | @classmethod | 112 | @classmethod | ||
113 | def can_build_together(cls, required_volume): | 113 | def can_build_together(cls, required_volume): | ||
114 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | 114 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | ||
115 | return total_volume >= required_volume | 115 | return total_volume >= required_volume |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|