n | | n | BASICS_DENSITY = { |
| | | 'Concrete': 2500, |
| | | 'Brick': 2000, |
| | | 'Stone': 1600, |
| | | 'Wood': 600, |
| | | 'Steel': 7700 |
| | | } |
| | | |
| class Material: | | class Material: |
| _DENSITY = 1 | | _DENSITY = 1 |
| | | |
| def __init__(self, mass): | | def __init__(self, mass): |
| self.mass = mass | | self.mass = mass |
| self.is_used = False | | self.is_used = False |
| | | |
| @property | | @property |
| def volume(self): | | def volume(self): |
| return self.mass / self._DENSITY | | return self.mass / self._DENSITY |
| | | |
| | | |
| class Concrete(Material): | | class Concrete(Material): |
n | _DENSITY = 2500 | n | _DENSITY = BASICS_DENSITY['Concrete'] |
| | | |
| | | |
| class Brick(Material): | | class Brick(Material): |
n | _DENSITY = 2000 | n | _DENSITY = BASICS_DENSITY['Brick'] |
| | | |
| | | |
| class Stone(Material): | | class Stone(Material): |
n | _DENSITY = 1600 | n | _DENSITY = BASICS_DENSITY['Stone'] |
| | | |
| | | |
| class Wood(Material): | | class Wood(Material): |
n | _DENSITY = 600 | n | _DENSITY = BASICS_DENSITY['Wood'] |
| | | |
| | | |
| class Steel(Material): | | class Steel(Material): |
n | _DENSITY = 7700 | n | _DENSITY = BASICS_DENSITY['Steel'] |
| | | |
| | | |
| class Factory: | | class Factory: |
n | _basics_density = { | n | """Create different building materials including some interesting 'alloys'. |
| 'Concrete': 2500, | | A fancy way to demonstrate the factory design pattern""" |
| 'Brick': 2000, | | |
| 'Stone': 1600, | | |
| 'Wood': 600, | | |
| 'Steel': 7700 | | |
| } | | |
| | | |
| _valid_classes = { | | _valid_classes = { |
| 'Concrete': Concrete, | | 'Concrete': Concrete, |
| 'Brick': Brick, | | 'Brick': Brick, |
| 'Stone': Stone, | | 'Stone': Stone, |
| 'Wood': Wood, | | 'Wood': Wood, |
| 'Steel': Steel | | 'Steel': Steel |
n | } # we start with the basic classes first and gradually add mixed ones | n | } # we start with the basic classes first and gradually add dynamic ones |
| | | |
| _all_produced_materials = [] | | _all_produced_materials = [] |
| | | |
| def __init__(self): | | def __init__(self): |
| self._produced_materials = [] | | self._produced_materials = [] |
| | | |
| def _handle_kwargs(self, **kwargs): | | def _handle_kwargs(self, **kwargs): |
| materials_list = [] | | materials_list = [] |
n | for key, value in kwargs.items(): | n | for material_name, mass in kwargs.items(): |
| if key not in self._valid_classes: | | if material_name not in self._valid_classes: |
| raise ValueError('Invalid class name!') | | raise ValueError('Invalid class name!') |
n | materials_list.append(self._valid_classes[key](value)) | n | materials_list.append(self._valid_classes[material_name](mass)) |
| self._produced_materials.extend(materials_list) | | self._produced_materials.extend(materials_list) |
| self._all_produced_materials.extend(materials_list) | | self._all_produced_materials.extend(materials_list) |
| return tuple(materials_list) | | return tuple(materials_list) |
| | | |
| @staticmethod | | @staticmethod |
| def _create_new_class(class_name, density): | | def _create_new_class(class_name, density): |
n | new_class = type( | n | new_class = type(class_name, (Material,), { '_DENSITY': density }) |
| class_name, (Material,), | | |
| { | | |
| '_DENSITY': density | | |
| } | | |
| ) | | |
| return new_class | | return new_class |
| | | |
| def _handle_args(self, *args): | | def _handle_args(self, *args): |
| total_mass = 0 | | total_mass = 0 |
| all_names = [] | | all_names = [] |
| | | |
| for arg in args: | | for arg in args: |
| if arg.is_used: | | if arg.is_used: |
| raise AssertionError('This material is already used in some factory!') | | raise AssertionError('This material is already used in some factory!') |
| arg_class_name = type(arg).__name__ | | arg_class_name = type(arg).__name__ |
| total_mass += arg.mass | | total_mass += arg.mass |
| split_names = arg_class_name.split('_') | | split_names = arg_class_name.split('_') |
| all_names.extend(split_names) | | all_names.extend(split_names) |
| | | |
| for arg in args: # in another loop to make sure no fails happen when we mix used and unused args | | for arg in args: # in another loop to make sure no fails happen when we mix used and unused args |
| arg.is_used = True | | arg.is_used = True |
| | | |
| all_names = sorted(all_names) | | all_names = sorted(all_names) |
| combined_name = '_'.join(all_names) | | combined_name = '_'.join(all_names) |
| | | |
| if combined_name not in self._valid_classes: | | if combined_name not in self._valid_classes: |
n | avg_density = sum(self._basics_density[name] for name in all_names) / len(all_names) | n | avg_density = sum(BASICS_DENSITY[name] for name in all_names) / len(all_names) |
| self._valid_classes[combined_name] = self._create_new_class(combined_name, avg_density) | | self._valid_classes[combined_name] = self._create_new_class(combined_name, avg_density) |
| | | |
| to_return = self._valid_classes[combined_name](total_mass) | | to_return = self._valid_classes[combined_name](total_mass) |
| | | |
| self._produced_materials.append(to_return) | | self._produced_materials.append(to_return) |
| self._all_produced_materials.append(to_return) | | self._all_produced_materials.append(to_return) |
| | | |
| return to_return | | return to_return |
| | | |
| def __call__(self, *args, **kwargs): | | def __call__(self, *args, **kwargs): |
n | if (len(args) == len(kwargs) == 0) or (len(args) != 0 and len(kwargs) != 0): | n | if (not args and not kwargs) or (args and kwargs): |
| raise ValueError('Invalid arguments!') | | raise ValueError('Invalid arguments!') |
n | | n | |
| if len(kwargs) != 0: | | if kwargs: |
| return self._handle_kwargs(**kwargs) | | return self._handle_kwargs(**kwargs) |
n | elif len(args) != 0: | n | elif args: |
| return self._handle_args(*args) | | return self._handle_args(*args) |
| | | |
| def can_build(self, target_volume): | | def can_build(self, target_volume): |
n | total_volume = 0 | n | total_volume = sum(material.volume for material in self._produced_materials if not material.is_used) |
| for material in self._produced_materials: | | |
| if not material.is_used: | | |
| total_volume += material.volume | | |
| return total_volume >= target_volume | | return total_volume >= target_volume |
| | | |
| @classmethod | | @classmethod |
| def can_build_together(cls, target_volume): | | def can_build_together(cls, target_volume): |
t | total_volume = 0 | t | total_volume = sum(material.volume for material in cls._all_produced_materials if not material.is_used) |
| for material in cls._all_produced_materials: | | |
| if not material.is_used: | | |
| total_volume += material.volume | | |
| return total_volume >= target_volume | | return total_volume >= target_volume |