f | class Material: | f | class Material: |
| """ | | """ |
| Define a material with a given mass and calculate its volume using a predefined density. | | Define a material with a given mass and calculate its volume using a predefined density. |
| """ | | """ |
| def __init__(self, mass): | | def __init__(self, mass): |
| self.mass = mass | | self.mass = mass |
| | | |
| @property | | @property |
| def volume(self): | | def volume(self): |
| return self.mass / self.DENSITY | | return self.mass / self.DENSITY |
| | | |
| | | |
| class Concrete(Material): | | class Concrete(Material): |
| DENSITY = 2500 | | DENSITY = 2500 |
| | | |
| | | |
| class Brick(Material): | | class Brick(Material): |
| DENSITY = 2000 | | DENSITY = 2000 |
| | | |
| | | |
| class Stone(Material): | | class Stone(Material): |
| DENSITY = 1600 | | DENSITY = 1600 |
| | | |
| | | |
| class Wood(Material): | | class Wood(Material): |
| DENSITY = 600 | | DENSITY = 600 |
| | | |
| | | |
| class Steel(Material): | | class Steel(Material): |
| DENSITY = 7700 | | DENSITY = 7700 |
| | | |
| | | |
| class Factory: | | class Factory: |
| """ | | """ |
| Manage the creation and combination of materials while enforcing reuse and uniqueness rules. | | Manage the creation and combination of materials while enforcing reuse and uniqueness rules. |
| """ | | """ |
| _used_instances = [] | | _used_instances = [] |
n | | n | |
| _dynamic_classes = {} | | _dynamic_classes = {} |
n | | n | |
| _all_factories = [] | | _all_factories = [] |
n | | n | |
| _all_base_materials = { | | _all_base_materials = { |
| "Concrete": Concrete, | | "Concrete": Concrete, |
| "Brick": Brick, | | "Brick": Brick, |
| "Stone": Stone, | | "Stone": Stone, |
| "Wood": Wood, | | "Wood": Wood, |
| "Steel": Steel, | | "Steel": Steel, |
| } | | } |
| | | |
| def __init__(self): | | def __init__(self): |
| self._created_materials = [] | | self._created_materials = [] |
n | Factory._all_factories.append(self) | n | self._all_factories.append(self) |
| | | |
| def __call__(self, *args, **kwargs): | | def __call__(self, *args, **kwargs): |
| if not args and not kwargs: | | if not args and not kwargs: |
| raise ValueError("Cannot have an instance without an argument or arguments.") | | raise ValueError("Cannot have an instance without an argument or arguments.") |
| if args and kwargs: | | if args and kwargs: |
| raise ValueError("There cannot be an instance with a mixture of positional and named arguments.") | | raise ValueError("There cannot be an instance with a mixture of positional and named arguments.") |
| | | |
| if args: | | if args: |
| return self._create_from_args(*args) | | return self._create_from_args(*args) |
| return self._create_from_kwargs(**kwargs) | | return self._create_from_kwargs(**kwargs) |
| | | |
| def _create_from_args(self, *args): | | def _create_from_args(self, *args): |
| if not self._validate_unused_materials(args): | | if not self._validate_unused_materials(args): |
| raise AssertionError("One or more materials have been used.") | | raise AssertionError("One or more materials have been used.") |
n | Factory._used_instances.extend(args) | n | self._used_instances.extend(args) |
| | | |
| components = {} | | components = {} |
n | | n | |
| for material in args: | | for material in args: |
| material_name = material.__class__.__name__ | | material_name = material.__class__.__name__ |
n | if material_name in Factory._all_base_materials.keys(): | n | if material_name in self._all_base_materials.keys(): |
| components[material_name] = Factory._all_base_materials[material_name] | | components[material_name] = self._all_base_materials[material_name] |
| else: | | else: |
| class_components = material.__class__.__name__.split("_") | | class_components = material.__class__.__name__.split("_") |
| for component_name in class_components: | | for component_name in class_components: |
n | components[component_name] = Factory._all_base_materials[component_name] | n | components[component_name] = self._all_base_materials[component_name] |
| | | |
| class_name = "_".join(sorted(components.keys())) | | class_name = "_".join(sorted(components.keys())) |
| | | |
n | if class_name not in Factory._dynamic_classes.keys(): | n | if class_name not in self._dynamic_classes.keys(): |
| self._create_dynamic_material(class_name, components) | | self._create_dynamic_material(class_name, components) |
| | | |
| total_mass = 0 | | total_mass = 0 |
| for material in args: | | for material in args: |
| total_mass += material.mass | | total_mass += material.mass |
| new_material = self._dynamic_classes[class_name](total_mass) | | new_material = self._dynamic_classes[class_name](total_mass) |
| self._created_materials.append(new_material) | | self._created_materials.append(new_material) |
| return new_material | | return new_material |
| | | |
| def _create_from_kwargs(self, **kwargs): | | def _create_from_kwargs(self, **kwargs): |
| created_instances = [] | | created_instances = [] |
| for name, mass in kwargs.items(): | | for name, mass in kwargs.items(): |
| if not self._validate_material_name(name): | | if not self._validate_material_name(name): |
| raise ValueError("Invalid material name") | | raise ValueError("Invalid material name") |
n | if name in Factory._all_base_materials: | n | if name in self._all_base_materials: |
| material_class = Factory._all_base_materials[name](mass) | | material_class = self._all_base_materials[name](mass) |
| created_instances.append(material_class) | | created_instances.append(material_class) |
n | elif name in Factory._dynamic_classes: | n | elif name in self._dynamic_classes: |
| material_class = Factory._dynamic_classes[name](mass) | | material_class = self._dynamic_classes[name](mass) |
| created_instances.append(material_class) | | created_instances.append(material_class) |
n | | n | |
| self._created_materials.extend(created_instances) | | self._created_materials.extend(created_instances) |
| return tuple(created_instances) | | return tuple(created_instances) |
| | | |
| def _create_dynamic_material(self, name, components): | | def _create_dynamic_material(self, name, components): |
| total_density = 0 | | total_density = 0 |
| for current in components.values(): | | for current in components.values(): |
| total_density += current.DENSITY | | total_density += current.DENSITY |
n | | n | |
| if len(components): | | if len(components): |
| num_components = len(components) | | num_components = len(components) |
| else: | | else: |
| num_components = 1 | | num_components = 1 |
| average_density = total_density / num_components | | average_density = total_density / num_components |
n | | n | |
| sorted_component_names = "_".join(sorted(components.keys())) | | |
| dynamic_material = type(sorted_component_names, | | dynamic_material = type(name, |
| (Material,), | | (Material,), |
| { | | { |
| 'DENSITY': average_density | | 'DENSITY': average_density |
| } | | } |
| ) | | ) |
n | Factory._dynamic_classes[sorted_component_names] = dynamic_material | n | self._dynamic_classes[name] = dynamic_material |
| | | |
| def _validate_unused_materials(self, materials): | | def _validate_unused_materials(self, materials): |
n | return all(material not in Factory._used_instances for material in materials) | n | return all(material not in self._used_instances for material in materials) |
| | | |
| def _validate_material_name(self, name): | | def _validate_material_name(self, name): |
n | return name in Factory._all_base_materials or name in Factory._dynamic_classes | n | return name in self._all_base_materials or name in self._dynamic_classes |
| | | |
| def can_build(self, wall_volume): | | def can_build(self, wall_volume): |
| current_volume = 0 | | current_volume = 0 |
| for material in self._created_materials: | | for material in self._created_materials: |
n | if material not in Factory._used_instances: | n | if material not in self._used_instances: |
| current_volume += material.volume | | current_volume += material.volume |
| return current_volume >= wall_volume | | return current_volume >= wall_volume |
n | | n | |
| @classmethod | | @classmethod |
| def can_build_together(cls, wall_volume): | | def can_build_together(cls, wall_volume): |
| total_volume = 0 | | total_volume = 0 |
| for factory in cls._all_factories: | | for factory in cls._all_factories: |
| for material in factory._created_materials: | | for material in factory._created_materials: |
| if material not in cls._used_instances: | | if material not in cls._used_instances: |
| total_volume += material.volume | | total_volume += material.volume |
| return total_volume >= wall_volume | | return total_volume >= wall_volume |
t | | t | |