Уведомления

Группа в Telegram: @pythonsu

#1 Март 8, 2019 18:07:01

zlodiak
От: Россия
Зарегистрирован: 2014-01-19
Сообщения: 159
Репутация: +  0  -
Профиль   Адрес электронной почты  

и снова о паттерне "стратегия"

ребята, помогите пожалуйста разобраться с паттерном стратегия. я готовлюсь к экзамену и , как мне кажется, изучил вопрос. побудьте преподами и оцените мои знания. возможно, есть пробелы, которые нужно восполнить.

мой ответ будет приблизительно таким:

паттерн стратегия помогает определить поведение объекта в зависимости от его типа. реализуется это через механизм наследования или композиции на этапе создания объекта.

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

1.
например существует программа-игра. она испльзует набор картинок. в зависимости от географического положения пользователя картинки тянутся из БД или сервиса flickr.

перед началом игры при помощи геолокаци определяется местоположение пользователя и на основе этого результата выбирается источник картинок. этот пример иллюстрирует следующий код(геолокацию я опустил, она не иммеет непосредственного отношения к паттерну):

 class ImageFinder():   
    def __init__(self, strategy=None):
        self.action = None
        if strategy:
            self.action = strategy()
    
    def find(self, image):
        if(self.action):
            return self.action.find(image)
        else: 
            raise UnboundLocalError('no strategyClass!')
class ImageFinderFlickr():
    def find(self, image):
        return "Found image in Flickr: " + image
class ImageFinderDatabase():
    def find(self, image):
        return "Found image in database: " + image
    
finderBase = ImageFinder()
finderFlickr = ImageFinder(strategy=ImageFinderFlickr)
finderDatabase = ImageFinder(strategy=ImageFinderDatabase)
try:
    print(finderBase.find('chickens'))
except Exception as e:
    print("exception :", e)
    
print(finderFlickr.find('chickens'))
print(finderDatabase.find('dogs'))


2.
пусть существует игра типа ГТА, в кторой есть персонажи, которые пишут ручкой, а есть которые рисуют кистью. на этапе создания этих персонажей каждому в атрибут жётско прописывается стратегия действия. в результате имеем несколько персонажей, поведение которых различается:

 class People():
    tool = None
    
    def __init__(self, name):
        self.name = name
    
    def setTool(self, tool):
        self.tool = tool
    
    def write(self, text):
        self.tool.write(self.name, text)
    
class ToolBase:
    def write(self, name, text):
        raise NotImplementedError()
    
class PenTool(ToolBase):
    def write(self, name, text):
        print('%s (ручкой) %s' % (name, text))
    
class BrushTool(ToolBase):
    def write(self, name, text):
        print('%s (кистью) %s' % (name, text))
    
class Student(People):
    tool = PenTool()
    
class Painter(People):
    tool = BrushTool()
maxim = Student(u'Максим')
maxim.write(u'Пишу лекцию о паттерне Стратегия')
# Максим (ручкой) Пишу лекцию о паттерне Стратегия
sasha = Painter(u'Саша')
sasha.write(u'Рисую иллюстрацию к паттерну Стратегия')
# Саша (кистью) Рисую иллюстрацию к паттерну Стратегия


3.
допустим, завод выпускает градусники, кторые могут показывать температуру в кельвинах и градусах фаренгейта. после покупки градусник единожды настраивается на нужные единицы, после чего может производить измерение температуры. в этом случае можно систему описать так:

 from abc import ABC, abstractmethod
class Termometer():
    def __init__(self, strategy):
        self.strategy = strategy()
    def calculate(self):
        try:
            print(self.strategy.calculate())
        except:
            print('no straregy error')
class Gradus(ABC):
    @abstractmethod
    def calculate(self):
        pass
class Farenheit(Gradus):
    def calculate(self):
        return 'farenheit value'
class Kelvin(Gradus):
    def calculate(self):
        return 'kelvin value'   
class Celsius(Gradus):
      pass
termometer1 = Termometer(Farenheit)     
termometer1.calculate()
termometer2 = Termometer(Kelvin)     
termometer2.calculate()
# error
# termometer3 = Termometer(Celsius)     
# termometer3.calculate()
как думаете, ответ был полный и понятный или есть места, на которые стоит обраттиь внимание и доработать.

буду благодарен, если зададите каверзные вопросы по теме этого паттерна, которые я могу услышать от преподавателя

Отредактировано zlodiak (Март 8, 2019 18:08:40)

Офлайн

#2 Март 8, 2019 18:39:03

FishHook
От:
Зарегистрирован: 2011-01-08
Сообщения: 8312
Репутация: +  568  -
Профиль   Отправить e-mail  

и снова о паттерне "стратегия"

zlodiak
паттерн стратегия помогает определить поведение объекта в зависимости от его типа. реализуется это через механизм наследования или композиции на этапе создания объекта.
Ой ненене! Стратегия - это инкапсуляция алгоритма, т.е. отделение алгоритма от объекта этот алгоритм использующий.
Это вообще основа ООП-проектирования, так называемый “принцип единичной ответственности”. Простыми словами, класс должен делать что-то одно. Стратегия позволяет объекту использующему стратегию не беспокоиться о том, какой именно алгоритм реализован в данной стратегии - он просто вызывает некий метод объекта, главное чтобы объект этот метод имел, а что именно метод делает (и какой у него конкретный тип!!!!!) - это ответственность объекта-стратегии.
В питоне шаблоны проектирования вообще плохо работают, тут нет статической типизации объектов, поэтому смысл стратегии размывается, и многие паттерны кажутся вообще лишними. В питоне можно не беспокоиться о типах (но это ведет к ошибкам в рантайме, что гораздо хуже) и интерфейсах (в питоне их вообще нет), но я попфтаюсь объяснить на примере.
Вот пример как делать плохо

 def print_to_file(data):
    with open("filename") as f:
        f.write(data)
   
def print_to_console(data):
    print(data)
   
class Foo:
  
    def __init__(self, log_type):
        self.log_type = log_type
  
    def do1(self):
        if self.log_type == "file":
            print_to_file("11111")
        else:
            print_to_console("11111")
  
    def do2(self):
        if self.log_type == "file":
            print_to_file("22222")
        else:
            print_to_console("2222")

Вот так лучше
 import abc
  
class Logger(metaclass=abc.ABCMeta):
  
    @abc.abstractmethod
    def log(self, data):
        ...
  
class FileLogger(Logger):
    def log(self, data):
        with open("filename") as f:
            f.write(data)
              
class ConsoleLOgger(Logger):
    
    def log(self, data):
        print(data)
      
class Foo:
    def __init__(self, logger: Logger):
        self.logger = logger
  
    def do1(self):
        self.logger.log("11111")
  
    def do2(self):
        self.logger.log("22222")
  
f = Foo(FileLogger())
  
f.do1()
f.do2()
  

Во втором случае реализована стратегия, и она не “помогает определить поведение объекта в зависимости от его типа”, класс Foo вообще ничего не знает о том, какие есть стратегии и что они делают, он тупо использует какой-то объект-зависимость и ему наплевать какую именно стратегию он реализует. Он никак не изменится, если, скажем, добавятся еще стратегии или наоборот, убавятся. Он самодостаточен, пока стратегии реализуют интерфейс (в нашем случае абстрактный класс). А сколько их, что они делают и как его не касается, он исполняет только свою ответственность и все.



Отредактировано FishHook (Март 8, 2019 18:59:54)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version