수색…


소개

디자인 패턴은 소프트웨어 개발에서 일반적으로 발생하는 문제에 대한 일반적인 해결책입니다. 이 문서는 특히 Python에서 일반적인 디자인 패턴의 예제를 제공하는 것을 목적으로합니다.

전략 패턴

이 디자인 패턴을 전략 패턴이라고합니다. 알고리즘 패밀리를 정의하고, 각 패밀리를 캡슐화하고, 상호 교환 가능하게 만드는 데 사용됩니다. 전략 디자인 패턴을 사용하면 알고리즘을 사용하는 클라이언트와 알고리즘을 독립적으로 변경할 수 있습니다.

예를 들어 동물은 여러 가지 다른 방식으로 "걸을 수 있습니다". 걷는 것은 여러 유형의 동물에 의해 실행되는 전략으로 간주 될 수 있습니다.

from types import MethodType


class Animal(object):
    
    def __init__(self, *args, **kwargs):
        self.name = kwargs.pop('name', None) or 'Animal'
        if kwargs.get('walk', None):
            self.walk = MethodType(kwargs.pop('walk'), self)

    def walk(self):
        """
        Cause animal instance to walk
        
        Walking funcionallity is a strategy, and is intended to
        be implemented separately by different types of animals.
        """
        message = '{} should implement a walk method'.format(
            self.__class__.__name__)
        raise NotImplementedError(message)


# Here are some different walking algorithms that can be used with Animal
def snake_walk(self):
    print('I am slithering side to side because I am a {}.'.format(self.name))

def four_legged_animal_walk(self):
    print('I am using all four of my legs to walk because I am a(n) {}.'.format(
        self.name))

def two_legged_animal_walk(self):
    print('I am standing up on my two legs to walk because I am a {}.'.format(
        self.name))

이 예제를 실행하면 다음과 같은 결과가 출력됩니다.

generic_animal = Animal()
king_cobra = Animal(name='King Cobra', walk=snake_walk)
elephant = Animal(name='Elephant', walk=four_legged_animal_walk)
kangaroo = Animal(name='Kangaroo', walk=two_legged_animal_walk)

kangaroo.walk()
elephant.walk()
king_cobra.walk()
# This one will Raise a NotImplementedError to let the programmer
# know that the walk method is intended to be used as a strategy.
generic_animal.walk()

    # OUTPUT:
    #
    # I am standing up on my two legs to walk because I am a Kangaroo.
    # I am using all four of my legs to walk because I am a(n) Elephant.
    # I am slithering side to side because I am a King Cobra.
    # Traceback (most recent call last):
    #   File "./strategy.py", line 56, in <module>
    #     generic_animal.walk()
    #   File "./strategy.py", line 30, in walk
    #     raise NotImplementedError(message)
    # NotImplementedError: Animal should implement a walk method 

C ++이나 Java와 같은 언어에서는이 패턴이 추상 클래스 나 인터페이스를 사용하여 구현되어 전략을 정의합니다. 파이썬에서는 types.MethodType 사용하여 클래스에 동적으로 추가 할 수있는 일부 함수를 외부에서 정의하는 것이 더 적합합니다.

디자인 패턴 및 싱글 톤 패턴 소개

디자인 패턴은 소프트웨어 디자인에서 commonly occurring problems 에 대한 솔루션을 제공합니다. 디자인 패턴은 GoF(Gang of Four) 의해 처음 소개되었으며 여기서 일반적인 패턴을 계속해서 반복해서 발생하는 문제로 설명하고 이러한 문제에 대한 해결책을 제시했습니다.

디자인 패턴에는 네 가지 필수 요소가 있습니다.

  1. The pattern name 은 디자인 문제, 솔루션 및 결과를 한두 단어로 설명하는 데 사용할 수있는 핸들입니다.
  2. The problem 는 패턴 적용시기를 설명합니다.
  3. The solution 은 디자인, 관계, 책임 및 협업을 구성하는 요소를 설명합니다.
  4. The consequences 는 패턴 적용의 결과와 절충 사항입니다.

디자인 패턴의 장점 :

  1. 여러 프로젝트에서 재사용 할 수 있습니다.
  2. 문제의 아키텍처 수준을 해결할 수 있습니다.
  3. 시간과 품질이 검증되었으며, 개발자와 건축가의 경험입니다.
  4. 그들은 신뢰성과 의존성을 가지고있다.

디자인 패턴은 세 가지 범주로 분류 할 수 있습니다.

  1. 창조적 인 본
  2. 구조 패턴
  3. 행동 패턴

Creational Pattern - 객체 생성 방법과 객체 생성의 세부 사항을 구분합니다.

Structural Pattern - 클래스와 객체의 구조를 디자인하여 더 큰 결과를 얻을 수 있도록 구성합니다.

Behavioral Pattern - 객체 간의 상호 작용과 객체의 책임에 관심이 있습니다.

싱글 톤 패턴 :

주어진 유형의 하나의 객체 만 가질 수있는 메커니즘을 제공하고 전역 액세스 지점을 제공하는 유형의 creational pattern 입니다.

예를 들어 싱글 톤은 데이터베이스 연산에서 사용될 수 있습니다. 여기서 우리는 데이터베이스 객체가 데이터 일관성을 유지하기를 원합니다.

이행

우리는 Singleton 클래스의 인스턴스를 하나만 만들고 동일한 객체를 다시 제공함으로써 Python에서 Singleton Pattern을 구현할 수 있습니다.

class Singleton(object):
    def __new__(cls):
        # hasattr method checks if the class object an instance property or not.
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance

s = Singleton()
print ("Object created", s)

s1 = Singleton()
print ("Object2 created", s1)

산출:

('Object created', <__main__.Singleton object at 0x10a7cc310>)
('Object2 created', <__main__.Singleton object at 0x10a7cc310>)

C ++ 또는 Java와 같은 언어에서이 패턴은 생성자를 비공개로 만들고 객체 초기화를 수행하는 정적 메서드를 만들어 구현됩니다. 이렇게하면 첫 번째 호출에서 하나의 객체가 만들어지고 클래스는 이후 동일한 객체를 반환합니다. 하지만 파이썬에서는 개인 생성자를 만들 수있는 방법이 없습니다.

공장 패턴

공장 패턴은 또한 Creational pattern 입니다. factory 라는 용어는 클래스가 다른 유형의 객체를 생성하는 것을 의미합니다. 객체와 메소드가 연결된 팩토리로 작동하는 클래스가 있습니다. 클라이언트는 특정 매개 변수를 사용하여 메소드를 호출하여 객체를 만들고 팩토리를 생성하여 원하는 유형의 객체를 만들어 클라이언트에 반환합니다.

from abc import ABCMeta, abstractmethod

class Music():
    __metaclass__ = ABCMeta
    @abstractmethod
    def do_play(self):
        pass

class Mp3(Music):
    def do_play(self):
        print ("Playing .mp3 music!")
    
class Ogg(Music):
    def do_play(self):
        print ("Playing .ogg music!")
    
class MusicFactory(object):
    def play_sound(self, object_type):
        return eval(object_type)().do_play()
    
if __name__ == "__main__":
    mf = MusicFactory()
    music = input("Which music you want to play Mp3 or Ogg")
    mf.play_sound(music)

산출:

Which music you want to play Mp3 or Ogg"Ogg"
Playing .ogg music!

MusicFactory 는 사용자가 제공하는 선택에 따라 Mp3 또는 Ogg 유형의 객체를 만드는 팩토리 클래스입니다.

대리

프록시 객체는 종종 다른 객체에 대한 보안 된 액세스를 보장하는 데 사용됩니다. 내부 비즈니스 로직은 안전 요구 사항으로 인해 오염되기를 원하지 않습니다.

특정 권한을 가진 사용자 만 리소스에 액세스 할 수 있음을 보장하고자한다고 가정합니다.

프록시 정의 : (실제로 예약을 볼 수있는 사용자 만이 consumer reservation_service에 액세스 할 수 있습니다)

from datetime import date
from operator import attrgetter

class Proxy:
    def __init__(self, current_user, reservation_service):
        self.current_user = current_user
        self.reservation_service = reservation_service

    def highest_total_price_reservations(self, date_from, date_to, reservations_count):
        if self.current_user.can_see_reservations:
            return self.reservation_service.highest_total_price_reservations(
                date_from,
                date_to,
                reservations_count
              )
        else:
            return []

#Models and ReservationService:

class Reservation:
    def __init__(self, date, total_price):
        self.date = date
        self.total_price = total_price

class ReservationService:
    def highest_total_price_reservations(self, date_from, date_to, reservations_count):
        # normally it would be read from database/external service
        reservations = [
            Reservation(date(2014, 5, 15), 100),
            Reservation(date(2017, 5, 15), 10),
            Reservation(date(2017, 1, 15), 50)
        ]

        filtered_reservations = [r for r in reservations if (date_from <= r.date <= date_to)]

        sorted_reservations = sorted(filtered_reservations, key=attrgetter('total_price'), reverse=True)

        return sorted_reservations[0:reservations_count]


class User:
    def __init__(self, can_see_reservations, name):
        self.can_see_reservations = can_see_reservations
        self.name = name

#Consumer service:

class StatsService:
    def __init__(self, reservation_service):
        self.reservation_service = reservation_service

    def year_top_100_reservations_average_total_price(self, year):
        reservations = self.reservation_service.highest_total_price_reservations(
            date(year, 1, 1),
            date(year, 12, 31),
            1
        )

        if len(reservations) > 0:
            total = sum(r.total_price for r in reservations)

            return total / len(reservations)
        else:
            return 0

#Test:
def test(user, year):
    reservations_service = Proxy(user, ReservationService())
    stats_service = StatsService(reservations_service)
    average_price = stats_service.year_top_100_reservations_average_total_price(year)
    print("{0} will see: {1}".format(user.name, average_price))

test(User(True, "John the Admin"), 2017)
test(User(False, "Guest"),         2017)

은혜
  • 액세스 제한이 변경되면 ReservationService 변경 사항을 피할 것입니다.
  • 우리는 비즈니스 관련 데이터 ( date_from , date_to , reservations_count )를 서비스에서 도메인과 무관 한 개념 (사용자 권한)과 혼합하지 않습니다.
  • Consumer ( StatsService )는 사용 권한 관련 논리가 없습니다.

주의 사항
  • 프록시 인터페이스는 항상 숨겨진 개체와 동일하므로 프록시로 래핑 된 서비스를 사용하는 사용자는 프록시 존재를 인식하지 못합니다.


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow