Comments 36
Ваш функциональный «наблюдатель» отличается от объектного только переносом состояния в область глобальных переменных.
+11
Ну да, это простейший случай. Если нужно несколько наблюдателей, можно добавить ещё один параметр и в subscribe и в notify, обозначающий, какой из наблюдателей использовать.
+1
Вообще, одна из главных особенностей ООП и Паттернов — отпадает необходимость править код при расширении функционала. Код не изменяется, а именно расширяется.
0
Боюсь, при переходе от глобального хранилища подписок к локальному, нам придется вносить изменения в код наблюдателя, независимо от парадигмы. Если вы об изменениях в коде клиентов, то они не обязательны. Хочу указываю доп.параметр, хочу нет — в дефолтном случае будет использоваться всё то же глобальное хранилище.
+1
На Пайтоне вы используете ООП, даже когда вам кажется, что вы его не используете. Вот эти ваши «функции» можно вызвать как
import имяфайла
имяфайла.run_claim()
-1
Т.е. по вашему наличие тут точки — признак ООП?
+7
Именно это точка в Пайтоне и значит. Вы от этого класс-объекта можете наследоваться и так далее.
-2
Ну вот возьмите для примера Haskell. Там можем написать
То, что модули в питоне объекты — это, условно говоря, деталь реализации.
Ведь и функции объекты, и коллекции, и даже числа.
Вы же не будете утверждать, что это ООП-код (дада, тут используются объекты)?
import Module
-- ...
xxx = Module.function_name
Тут тоже ООП? Или в чем отличие от Python?То, что модули в питоне объекты — это, условно говоря, деталь реализации.
Ведь и функции объекты, и коллекции, и даже числа.
Вы же не будете утверждать, что это ООП-код (дада, тут используются объекты)?
print(1 + 2)
+5
На самом деле, завит от контекста, в котором мы используем «объект».
конечно не ООП.
А когда мы применяем магию вроде
или
то резко замечаем, что и модуль и функция тоже объекты.
Мне кажется, тут надо разделять «использование ООП» и «возможность использования ООП».
xxx = Module.function_name
конечно не ООП.
А когда мы применяем магию вроде
from types import ModuleType
class(ModuleType):
...
или
function_name.newattr = xxx
то резко замечаем, что и модуль и функция тоже объекты.
Мне кажется, тут надо разделять «использование ООП» и «возможность использования ООП».
+2
Причём тут Хаскель? В Пайтоне — ООП, в Хаскеле мне всё равно что.
-3
Да в принципе и не при чем. Главное не говорить на собеседованиях «ООП — это когда точку ставишь, а потом такой список доступных методов вылазит».
+2
Я про Пайтон говорю, в Пайтоне это так.
-1
Старше меня, а такую чушь несёте, да ещё так упор(но|ото). ООП — это объектно-ОРИЕНТИРОВАННОЕ программирование. Вызов обычной функции через точку, как «метод» модуля — не тот случай. Можно написать целый модуль из чистых (в смысле ФП) функций, и все их вызывать через точку, но это не будет ООП, т.к. ООП — это парадигма, а не синтаксис. На ассемблере тоже есть ООП, однако нет точки:
;; object.method(arg1, arg2)
push object
push arg1
push arg2
call method
;; object.method(arg1, arg2)
push object
push arg1
push arg2
call method
0
Младше меня, а такую чушь несёте. Причём тут возраст вообще?
Вы, вот, например, путаете ФП и процедурный стиль.
На Ассемблере, очевидным образом, ООП нет, так как нет инкапсуляции и наследования.
Так как написание файла с функциями почти равно на Пайтоне написанию класса с методами, то это, конечно же, ООП.
Если вы накидаете бессистемно класс в методами, отнаследовавшись от чего-либо — это не ООП будет что ли? Файл с функциями ровно так и выглядит внутри Пайтона (да и для программиста — тоже).
Вы, вот, например, путаете ФП и процедурный стиль.
На Ассемблере, очевидным образом, ООП нет, так как нет инкапсуляции и наследования.
Так как написание файла с функциями почти равно на Пайтоне написанию класса с методами, то это, конечно же, ООП.
Если вы накидаете бессистемно класс в методами, отнаследовавшись от чего-либо — это не ООП будет что ли? Файл с функциями ровно так и выглядит внутри Пайтона (да и для программиста — тоже).
-2
От класса с методами отнаследоваться далее можно. А от модуля с функциями уже нельзя. Вы не сможете (без магии) определить модуль, отсутствующие атрибуты которого будут искаться в «родительском» модуле. Так что модуль — это всё таки экземпляр класса, но не сам класс. Т.е. наследования, так необходимого для ООП, здесь нет.
0
О какой магии речь идёт?
Вы точно о Пайтоне говорите?
a.py:
b.py:
Запускаем:
Вы тут не видите принципы ООП что ли?
Вы точно о Пайтоне говорите?
a.py:
attrib1="a1"
attrib2="a2"
def method1():
print("a1")
def method2():
print("a2")
def _private():
print("private")
b.py:
from a import *
def method2():
print("b2")
def method3():
print("b3")
attrib2="b2"
method1()
method2()
method3()
print(attrib1, attrib2)
_private()
Запускаем:
a1
b2
b3
a1 b2
Traceback (most recent call last):
File "b.py", line 17, in <module>
_private()
NameError: name '_private' is not defined
Вы тут не видите принципы ООП что ли?
-1
Нет, не вижу. Вы просто скопировали атрибуты одного модуля в другой, наследственной связи не возникло.
Если после ваших манипуляций выполнить
Если после ваших манипуляций выполнить
a.method1 = another_method
, то b.method1
никак не изменится.0
А какой принцип ООП требует, чтобы что-то изменилось в этом случае?
0
Тот самый, который вы пытаетесь проиллюстрировать. Мы ведь о наследовании говорим?
Все свойства подкласса, которые он не перезаписывал, должны всегда соответствовать свойствам родителя.
По-русски: Если в классе «Гражданин» во время выполнения программы изменился метод «расчет НДФЛ», то это автоматически должно отразиться на всех подклассах — и «Программист» и «Врач» и всех остальных. Кроме, может быть, специального класса «Льготный гражданин», в котором определено, что он НДФЛ считает не как все.
Все свойства подкласса, которые он не перезаписывал, должны всегда соответствовать свойствам родителя.
По-русски: Если в классе «Гражданин» во время выполнения программы изменился метод «расчет НДФЛ», то это автоматически должно отразиться на всех подклассах — и «Программист» и «Врач» и всех остальных. Кроме, может быть, специального класса «Льготный гражданин», в котором определено, что он НДФЛ считает не как все.
0
Они и соответствуют. То, что вы потом на хочу что-то изменили «задним числом» ни о чём не говорит. Это уже особенности реализации, а не принципы ООП.
0
Это не «на хочу», а совешенно необходимая вещь. Вместо метода, там мог бы быть какой-нибудь счётчик, или ещё какое-то изменяемое свойство, которое должно быть доступно для всех подклассов и их экземпляров.
Видимо у нас слишком разное понимание этих принципов. Похоже, что вы даже такой
или такой
код тоже отнесёте к проявлениям ООП.
Мне же ближе классическое определение от Алана Кея.
Подчеркну — я знаю, что в Python'е всё есть объект. И это очень удобно. Но писать на нём все же можно по-разному. Статья написана о стиле кодирования, а не об особенностях терминологии. Наверное надо было её назвать «паттерны без классов», а не «без ООП». Меньше возражений было бы.
Видимо у нас слишком разное понимание этих принципов. Похоже, что вы даже такой
dct.update(another_dict)
или такой
lst[:] = another_list
код тоже отнесёте к проявлениям ООП.
Мне же ближе классическое определение от Алана Кея.
Подчеркну — я знаю, что в Python'е всё есть объект. И это очень удобно. Но писать на нём все же можно по-разному. Статья написана о стиле кодирования, а не об особенностях терминологии. Наверное надо было её назвать «паттерны без классов», а не «без ООП». Меньше возражений было бы.
+1
Различных способов реализовать наследование или его подобие при отсутствии встроенной в язык поддержке ООП очень много. Покажите где хотя бы один из них используется в статье. У вас написано «
и так далее, но пока эти классы не начнут наследоваться друг от друга или хотя бы реализовывать одинаковые интерфейсы весь этот код будет процедурным, в котором просто для удобства к структурам были присобачены методы. В библиотеке с такими классами ООП нет. А классы есть.
Так что вопрос не в том, можно ли реализовать принципы ООП на модулях. Как уже сказано, ООП можно реализовать даже на ассемблере. Вопрос в том, где вы нашли ООП в статье?
from a import *
». У автора такого нет. Наличие возможности использования ООП тем или иным способам не делает любой код использующим ООП. Я, к примеру, могу написать пачку классов видаfrom math import acos, atan, sin, cos
class CoordCartezian:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def distance(self, c2):
return ((self.x-c2.x)**2 + (self.y-c2.y)**2 + (self.z-c2.z)**2) ** 0.5
def spherical(self):
r = (self.x*self.x + self.y*self.y + self.z*self.z) ** 0.5
return CoordSpherical(
r,
acos(self.z/r),
atan(self.y/self.x)
)
class CoordSpherical:
def __init__(self, r, t, p):
self.r = r
self.t = t
self.p = p
def angle(self, c2):
return acos(sin(self.t)*sin(self.p)*sin(c2.t)*sin(c2.p) + sin(self.t)*cos(self.p)*sin(c2.t)*cos(c2.p) + cos(self.t)*cos(c2.t))
def cartezian(self):
return CoordCartezian(
self.r * sin(self.t) * cos(self.p),
self.r * sin(self.t) * sin(self.p),
self.r * cos(self.t)
)
и так далее, но пока эти классы не начнут наследоваться друг от друга или хотя бы реализовывать одинаковые интерфейсы весь этот код будет процедурным, в котором просто для удобства к структурам были присобачены методы. В библиотеке с такими классами ООП нет. А классы есть.
Так что вопрос не в том, можно ли реализовать принципы ООП на модулях. Как уже сказано, ООП можно реализовать даже на ассемблере. Вопрос в том, где вы нашли ООП в статье?
0
Вы от этого класс-объекта можете наследоваться и так далее.
Неа, не можем:
Скрытый текст
In [1]: import itertools
In [2]: class MyItertools(itertools):
...: pass
...:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-2-8cefe4e16e35> in <module>()
----> 1 class MyItertools(itertools):
2 pass
TypeError: Error when calling the metaclass bases
module.__init__() takes at most 2 arguments (3 given)
+1
НЛО прилетело и не оставило здесь никакого сообщения.
+1
Ваш функциональный «наблюдатель» отличается от объектного только переносом состояния в область глобальных переменных.
Позвольте поправить. Не следует путать функциональщину с процедурщиной.
UPD: упс. Не посмотрел, что статья 2013 года. Привет из будущего
0
ООП это методология, классы лишь наиболее простое средства для реализации модели декларирующей объектные свойства. Это вовсе не означает, что ООП это классы. ООП можно реализовывать и на чистом С, в котором, как Вы наверное знаете, классов нет и в помине. Поэтому Ваш заголовок не соответствует действительности. Паттерн «Наблюдатель» это ООП паттерн по своей сути, и не важно какими средствами Вы реализовали эту абстрактную модель. Она от этого не перестанет быть ОО.
+14
Я согласен, что паттерн остаётся самим собой независимо от реализации. Перестаёт ли он от этого быть объектно-ориентированным… это пожалуй спорный вопрос и он может легко развиться в холивар в стиле «это мы придумали», «а у нас это уже было» и т.д. :)
Но пост как раз про реализацию. Паттерны в пособиях объясняются именно на примерах создания классов. И тот же человек пишущий на чистом C может не догадаться, что тоже может ими пользоваться. Поэтому я и решил предложить альтернативную версию. Для расширения понимания, так сказать.
Но пост как раз про реализацию. Паттерны в пособиях объясняются именно на примерах создания классов. И тот же человек пишущий на чистом C может не догадаться, что тоже может ими пользоваться. Поэтому я и решил предложить альтернативную версию. Для расширения понимания, так сказать.
0
Разумеется, в тех случаях, где функциональная парадигма изначально естественна, ее эмуляция методами ООП будет выглядеть более монстроподобно.
У ООПшного подхода есть так плюс как самодокументируемость. Что такое «словарь функций»? — как я могу догадаться, что в данном конкретном случае это «почта»? Только поддерживая консистентные имена переменных? Если в какое-нибудь место ваша «почта» будет передана просто как arg3 — что я смогу понять про ее назначение?
В то время как, скажем, интерфейс SubscriptionTopics говорит мне уже гораздо больше — независимо от того, каким путем он ко мне попал. Я даю имя сущности, которая мне нужна — в то время как вы называете реализацию сущности: в одном месте словарь функций может быть «почтой», в другом — частью конечного автомата — это очень разные сущности, а реализация одна.
У ООПшного подхода есть так плюс как самодокументируемость. Что такое «словарь функций»? — как я могу догадаться, что в данном конкретном случае это «почта»? Только поддерживая консистентные имена переменных? Если в какое-нибудь место ваша «почта» будет передана просто как arg3 — что я смогу понять про ее назначение?
В то время как, скажем, интерфейс SubscriptionTopics говорит мне уже гораздо больше — независимо от того, каким путем он ко мне попал. Я даю имя сущности, которая мне нужна — в то время как вы называете реализацию сущности: в одном месте словарь функций может быть «почтой», в другом — частью конечного автомата — это очень разные сущности, а реализация одна.
0
В «Состояние», таскаемый везде dict и есть объект. Это тот же ООП, только записанный иначе.
И с классами оно немного лаконичней — gist.github.com/nvbn/5830627 =)
И с классами оно немного лаконичней — gist.github.com/nvbn/5830627 =)
+3
Кажется, что использование паттерна Команда как простого коллбэка — это один из наиболее редких юзкейзов.
Основные, как мне кажется — объединить код и данные с целью манипуляции этими данными — например сохранить команды, чтобы иметь возможность повторно применить их к другому объекту; или иметь возможность откатить изменения (для этого в команде должен быть метод undo). Или сбросить команды на диск как «историю» изменений.
Более хитрый юзкейз — слияние однотипных подряд идущих команд в одно действие — тоже может быть полезен.
Основные, как мне кажется — объединить код и данные с целью манипуляции этими данными — например сохранить команды, чтобы иметь возможность повторно применить их к другому объекту; или иметь возможность откатить изменения (для этого в команде должен быть метод undo). Или сбросить команды на диск как «историю» изменений.
Более хитрый юзкейз — слияние однотипных подряд идущих команд в одно действие — тоже может быть полезен.
+1
Подписываем наши функции на рассылки:
>>> subscribe('insertors', fun)
>>> subscribe('insertors', bar)
ммда, такие примеры можно называть паттернами, в каком-то узком смысле, просто потому, что они достаточно типичны. но на роль хороших архитектурных приемов, они, по-моему, не годятся уже лет как тридцать.)
как выше заметил bolk, в статье перепутаны ФП и процедурное программирование — практически во всех примерах видны все стандартные грабли этого подхода, связанных с плохо инкапсулированным состоянием. как раз ООП с этим и борется, за счет «развесистых схем классов», а в ФП состояния не должно быть как такового (во всяком случае в таком совсем уж явном виде).
-1
Sign up to leave a comment.
Паттерны проектирования без ООП