Python Language
Шаблоны проектирования
Поиск…
Вступление
Шаблон стратегии
Этот шаблон дизайна называется 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)
где они описывали общие шаблоны как проблемы, возникающие снова и снова, и решения этих проблем.
Шаблоны проектирования имеют четыре основных элемента:
-
The pattern name
- это дескриптор, который мы можем использовать для описания проблемы проектирования, ее решений и последствий одним или двумя словами. -
The problem
когда применять шаблон. -
The solution
описывает элементы, которые составляют дизайн, их отношения, обязанности и сотрудничество. -
The consequences
являются результаты и компромиссы применения шаблона.
Преимущества шаблонов проектирования:
- Они многократно используются в нескольких проектах.
- Архитектурный уровень проблем можно решить
- Они проверены временем и хорошо зарекомендовали себя, это опыт разработчиков и архитекторов
- Они имеют надежность и зависимость
Шаблоны проектирования можно разделить на три категории:
- Шаблон создания
- Структурный рисунок
- Поведенческая картина
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
) также свободен от связанной с разрешениями логики
ПРЕДОСТЕРЕЖЕНИЯ
- Интерфейс прокси всегда точно такой же, как и объект, который он скрывает, поэтому пользователь, который потребляет услугу, завернутый прокси-сервером, даже не знал о наличии прокси-сервера.