Поиск…


Вступление

Шаблон проектирования является общим решением общей проблемы в разработке программного обеспечения. Этот раздел документации специально предназначен для предоставления примеров общих шаблонов проектирования в Python.

Шаблон стратегии

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

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

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

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

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

Реализация

Мы можем реализовать Singleton Pattern в 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 зависимости от выбора, который предоставляет пользователь.

полномочие

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

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

Определение прокси: (он гарантирует, что только пользователи, которые действительно могут видеть оговорки, смогут пользоваться услугой customer_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 ) также свободен от связанной с разрешениями логики

ПРЕДОСТЕРЕЖЕНИЯ
  • Интерфейс прокси всегда точно такой же, как и объект, который он скрывает, поэтому пользователь, который потребляет услугу, завернутый прокси-сервером, даже не знал о наличии прокси-сервера.


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow