Шаблоны проектирования

Введение

Примеры

Стратегия

Этот шаблон дизайна называется Стратегическим шаблоном. Он используется для определения семейства алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Шаблон разработки стратегии позволяет алгоритму варьироваться независимо от клиентов, которые его используют.

Например, животные могут «гулять» разными способами. Прогулку можно считать стратегией, которую реализуют разные виды животных:

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, этот шаблон реализован с использованием абстрактного класса или интерфейса для определения стратегии. В Python это имеет смысл только определить некоторые функции внешне , которые могут быть добавлены динамически в классе с использованием types.MethodType .

Введение в шаблоны проектирования и Singleton Pattern

Шаблоны обеспечивают решение commonly occurring problems в разработке программного обеспечения. Образцы дизайна впервые были введены GoF(Gang of Four) , где они описаны общие закономерности как проблемы , которые возникают снова и снова , и решение этих проблем.

Шаблоны проектирования имеют четыре основных элемента:

Преимущества дизайна выкройки:

  1. Они могут быть использованы в нескольких проектах.
  2. Архитектурный уровень проблем может быть решен
  3. Они проверены временем и хорошо себя зарекомендовали, что является опытом разработчиков и архитекторов.
  4. У них есть надежность и зависимость

Шаблоны проектирования можно разделить на три категории:

  1. Творческий Образец
  2. Структурная картина
  3. Поведенческая картина

Creational Pattern - Они обеспокоены тем , как объект может быть создан и они изолируют детали создания объекта.

Structural Pattern - Они разрабатывают структуру классов и объектов таким образом, чтобы они могли составить для достижения больших результатов.

Behavioral Pattern - Они связаны с взаимодействием между объектами и ответственности объектов.

Singleton Pattern:

Это тип creational pattern , который обеспечивает механизм , чтобы иметь только один и один объект данного типа и обеспечивает глобальную точку доступа.

Например, Singleton может использоваться в операциях с базой данных, где мы хотим, чтобы объект базы данных поддерживал согласованность данных.

Реализация

Мы можем реализовать шаблон Singleton в Python, создав только один экземпляр класса Singleton и снова предоставив тот же объект.

 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, этот шаблон реализуется путем обеспечения приватности конструктора и создания статического метода, выполняющего инициализацию объекта. Таким образом, один объект создается при первом вызове, а класс возвращает тот же объект после этого. Но в Python у нас нет никакого способа создавать частные конструкторы.

Фабричный образец

Завод шаблон также 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 в зависимости от выбора пользователя обеспечивает.

полномочие

Прокси-объект часто используется для обеспечения защищенного доступа к другому объекту, внутреннюю бизнес-логику которого мы не хотим загрязнять требованиями безопасности.

Предположим, мы хотим гарантировать, что только пользователь с определенными разрешениями может получить доступ к ресурсу.

Определение прокси: (оно гарантирует, что только пользователи, которые на самом деле могут видеть резервирования, будут иметь возможность потребителю booking_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 ) с доменными несвязанными понятиями (права пользователя) в процессе эксплуатации.
  • Потребитель ( StatsService ) свободен от разрешений , связанных логики , а также  ПРЕДОСТЕРЕЖЕНИЯ
  • Интерфейс прокси всегда точно такой же, как объект, который он скрывает, так что пользователь, который использует сервис, заключенный в прокси, даже не знал о наличии прокси.

Синтаксис

Параметры

Примечания