01. OCP (Open Close Principal)

"""
OCP 개방폐쇄원칙은 새로운 기능을 추가할 때 확장을 통해 추가하고, 수정을 통해서가 아니다.
"""

from enum import Enum


class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3


class Size(Enum):
    SMALL = 1
    MEDIUM = 2
    LARGE = 3


class Product:
    def __init__(self, name, color, size):
        self.name = name
        self.color = color
        self.size = size


# OCP = open for extension, closed for modification
# 확장을 위해서는 열려 있지만, 수정을 위해서는 클래스를 닫아야 한다.


class ProductFilter:
    """요구사항이 추가됨에 따라 계속해서 함수가 생겨나는것은 OCP 원칙에 위배된다."""

    def filter_by_color(self, products, color):
        for product in products:
            if product.color == color:
                yield product

    def filter_by_size(self, products, size):
        for product in products:
            if product.size == size:
                yield product

    def filter_by_size_and_color(self, products, size, color):
        for product in products:
            if product.color == color and product.size == size:
                yield product

    # 2 가지 옵션이 있다면 모든 경우의수 는 3 = 2C1 + 2C2
    # 3 가지 옵션이 있다면 모든 경우의수 는 7 = 3C1 + 3C2 + 3C3 (이름, 색상, 크기)
    # OCP 원칙은 새 필터를 추가할 때마다 필터함수를 추가하는 것이 아니라, 확장을 통해 추가한다는 것을 의미한다.


# Specification


class Specification:
    def is_satisfied(self, item):
        raise NotImplementedError

    def __and__(self, other):
        return AndSpecification(self, other)


class ColorSpecification(Specification):
    def __init__(self, color):
        self.color = color

    def is_satisfied(self, item):
        return item.color == self.color


class SizeSpecification(Specification):
    def __init__(self, size):
        self.size = size

    def is_satisfied(self, item):
        return item.size == self.size


class AndSpecification(Specification):
    def __init__(self, *args):
        self.args = args

    def is_satisfied(self, item):
        return all(map(lambda spec: spec.is_satisfied(item), self.args))


class Filter:
    def filter(self, items, spec):
        raise NotImplementedError


class BetterFilter(Filter):
    def filter(self, items, spec):
        for item in items:
            if spec.is_satisfied(item):
                yield item


if __name__ == "__main__":
    apple = Product("Apple", Color.GREEN, Size.SMALL)
    tree = Product("Tree", Color.GREEN, Size.LARGE)
    house = Product("House", Color.BLUE, Size.LARGE)

    products = [apple, tree, house]

    pf = ProductFilter()
    print("Green products (old):")
    for p in pf.filter_by_color(products, Color.GREEN):
        print(f" - {p.name} is green")

    print('--------------------------------')

    bf = BetterFilter()

    print("Green products (new):")
    green = ColorSpecification(Color.GREEN)
    for p in bf.filter(products, green):
        print(f" - {p.name} is green")

    print("Large products:")
    large = SizeSpecification(Size.LARGE)
    for p in bf.filter(products, large):
        print(f" - {p.name} is Large")

    print("Large Blue products:")
    # large_blue = AndSpecification(large, ColorSpecification(Color.BLUE))
    large_blue = large & ColorSpecification(Color.BLUE)
    for p in bf.filter(products, large_blue):
        print(f" - {p.name} is Large and Blue")


# OCP 개방 폐쇄 원칙은 기본적으로 코드를 계속 수정하는 상황을 피하고, 새롭게 만드는것.

댓글남기기