Pull to refresh

Comments 72

Странно, когда был студентом делал проще — ставил компьютер в биосе включаться в определеное время, звук включения Виндовс заменял песней Рамштайна. Все было ок.

Хотя согласен что нахождение альтернативных путей решения задачи всегда +10 ЧСВ.
Более того, можно было просто воспользоваться cron`ом или планировщиком в Windows. Но моя цель была — изучить и применить паттерн на практике. И то, что у меня не оказалось будильника под рукой — это просто случайность :) Если бы я забыл в гостях утюг. Я бы написал утюг (правда не знаю как, но можно было бы подумать).
Пару раз пробовал. Песню с удовольствием прослушивал до конца и спал дальше :)
тут всё от громкости зависит. Последний раз, когда я лицезрел этот подход, закончился тем что паренёк оказался на полу в другом конце комнаты в общаге абсолютно не понимая где он и что случилось.
Пришли соседи и надавали тумаков?
1. строка " 0 7 * * * play alarm.mp3" в кроне уже не модно?

2.
Cитуация осложнялась тем, что завтра в 8:00 надо было быть на работе.
действительно, самое логичное в этой ситуации не лечь по-раньше спать, а всю ночь писать будильник.
вот не поверите — не заметил ваш комент :)
тогда уж сделайте дисклаймер в топике.
Тогда уж at всё-таки, а не cron. Действие-то единовременное.
вставать на работу — единовременное действие? :)
Текст не читал после диаграммы, которая опять же суть не передала.

Хочу грамотные диаграммы.

Одна хорошая диаграмма стоит тысяч букв.
Возможно проблема в том, что я сразу привожу диаграммы конкретных практических задач. А в статьях и литературе обычно приводятся абстрактные диаграммы с соответствующими названиями классов, вроде AbstractionInterface, AbstractionImplInterface и т.д. Возможо есть смысл сначала приводить абстрактные диаграммы, а уже потом практические. Я учту это.
Как LookUpAlarmClock c ShellMp3AlarmClock связана?

Из диграммы об этом можно тока догадываться.
LookupAlarmClock — это уточнение абстракции. ShellMP3AlarmClock — уточнение реализации. Связаны они по ссылке bridge на AlarmClockImpl в интерфейсе AlarmClock.
На диаграмме этого нет.

Верхняя связь означает только, что у LookupAlarmClock есть некий атрибут bridge c типом AlarmClockImpl, но не типом ShellMP3AlarmClock.

Подсказываю уже, ибо устал: д.б. ещё одна связь на втором уровне и атрибут bridge повторён, но уже с конкретным типом.
В этом нет необходимости, механизм наследования это подразумевает. Более того, ссылка на базовый класс (AlarmClockImpl) одновременно является ссылкой и на все наследники этого класса.

Честно говоря, не понял, как можно «повторить» атрибут brigde но уже с другим типом. Он же наследуется. Наследуется как есть. Вопрос в другом. Какой именно тип там будет храниться. Этого сказать нельзя. Это даже не обязательно ShellMP3AlarmClock. Это просто ссылка на базовый класс.
Да это то очевидно, что в реальности в конструкторе будет что-то типа:

this.bridge = new ShellMP3AlarmClock();

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

Очень просто его повторять — копи-пасте и замена типа.
Поздравляю. Вы абсолютно ничего не поняли про паттерн Bridge.
Ммм… Дык объясните тогда грамотной UML-диаграммой, а не тонной слов.
В целом, мне проще отослать вас вот сюда. Хотя, если вы не поняли эту статью (с довольно понятной, к слову, UML-диаграммой классов), то, наверное, и статью по ссылке не поймете.
// Совет Вам научиться уважать собеседников.

А по теме, если, то я посмотрел на пример на C# и там неважно-что-которое-меняется на вход в конструктор идёт.

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

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

С т.з. «правильности» это необязательно, но с т.з. мгновенного понимания крайне полезно.
// Я стараюсь

Во первых, какие еще варианты с точки зрения мгновенного понимания могут быть? Я бы понял, если бы была альтернативная интерпретация этой диаграммы. Но ведь нету ее. Что еще может пойти на вход конструктору, если не подклассы абстрактного класса AlarmClockImpl?

Во-вторых, что еще за «простые стрелочки»? В UML есть агрегация, композиция, наследование, реализация и зависимость. Ваши «простые стрелочки» ни к одному из этих типов не относятся.
Дык Вам то это понятно, потому-что Вы текст читали!

А я тока картинку смотрел и не понял сходу. Понял, тока зайдя на википедию и то, потому-что там увидел в коде намёк, что конкретика м.б. миллион раз разная и поэтому тут такой полиморфизм получается.

Простые стрелочки — это зависимость. Т.е. не ромбик пустой/закрашенный, а именно простая стрелочка. Она как раз намекает, что класс А как-то использует класс Б: создаёт его, на вход получает и т.д.

Здесь для понимания подобные стрелочки полезны.
Я напоследок позанудствую. По вашей логике, если некий класс А работает с иерархией классов (имеющих базовый класс Б) через указатель/ссылку на базовый, нужно в UML-диаграмме провести зависимости из класса А во всех возможных потомков Б. Так вот, это неправильно. Зависимость А->Х означает «изменение спецификации Х влечет за собой изменение А». В нашем случае для А имеют значение только изменения Б, интерфейса к Х. А детали реализации его потомков (включая, возможно, публичные функции, не объявленные в Б) нам абсолютно не важны. Потому и стрелочки такие тут рисовать нельзя.
Ок. По вашей ссылке определение такое: «Зависимость существует, если класс Х — это параметр, член или локальная переменная в методе класса А». В случае паттерна Bridge наследники Б, в отличие от самого Б, под это определение не подходят.
См. Ваш же предыдущий пруфлинк на англопедию, где пример кода на C#.

Кто там у нас на входе? IDrawingApi?

Наследники Б на вход в итоге и поступают в реальности.

А реально на вход поступают уже объекты, которые этот интефейс реализуют. Так что для понимания эта стрелка нужна. Она обязана там быть, иначе FAIL.

Люди придумали диаграммы для избавления от over9000 букв и для понимания. Т.е. не для чтения over9000 букв и вычисления в уме, что в-принципе там это может быть, а именно для явного тычка, что оно там будет.
Вы не различаете реальный тип объекта, который передается на вход, и тип параметра функции. Тип объекта — не IDrawingApi (хотя бы потому, что это вообще интерфейс). Тип параметра конструктора — именно IDrawingApi. Далее вспоминаем все, что я говорил раньше, и понимаем, что стрелки не нужны.
С формальной т.з. вы правы, но с т.з. понятности — нет.
Это как раз тот случай, где надо мозг включить и выйти за рамки формальности.

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

Ещё раз, с т.з. формальной Вы правы, т.е. как академик к примеру, но как преподаватель для недалёких студентов — нет.

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

К тому же в первой версии диаграммы вааще тока один потомок был, что бессмысленно с т.з. этого паттерна.
Ну, я согласен с тем, что сама по себе диаграмма не слишком выразительна. Но это потому, что нужно читать сопроводительный текст. UML сам по себе не может все объяснить.
// Интересно, а сколько здесь уровней может быть? :)

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

Но вот для понимания это мало помогает.

Как вариант, сделать потомков как-то так: Потомок1, потом нечто символизирущее многоточие, и ПотомокN.

И в эти потомки стрелку uses. Хотя именно по «правильности» стрелки там быть не должно.
Это конечно мое субъективное мнение, но от слова «реализация» в глазах рябит немного. Я запутался =)
Но из того что понял появился такой вопрос:
У нас есть класс-мост. По одну сторону абстракции, по другую реализации. Класс-мост описывает в себе один большой жирный интерфейс, включающий кучу методов абстрактных классов или он просто защищает наследников(реализации) от сбоев при изменении абстрактного класса с другой стороны моста?
Не совсем так. Во первых мост — это не класс. Это название паттерна. Если говорить более конкретно мост — это 2 интерфейса: Интерфейс абстракции и интерфейс реализации. Причем, интерфейс абстракции содержит ссылку на интерфейс реализации, для делегирования методов оной. Важно понимать, что это два различных интерфейса, с различным набором методов, но реализующих похожие вещи. Абстракция содержит более общие методы (например, toWake() — разбудить). Реализация — простейшие методы (ring() — звонить, может быть showNotifycation() оповестить пользователя). При этом, абстракция использует эти методы для своих корыстных нужд.
Мыслите очень необычно, я ценю это в людях. Написать будильник для того, чтобы лечь спать — это круто.
Проблема в том, что пример для этого паттерна не годится, ну почти. Я был бы доволен если бы вы привели другой пример, который более явно описывал паттерн мост как мост, выделялся бы среди будильника или что бы там было. Писать реализацию для того, что бы показать паттерн это одно, а вот применить паттерн для того чтобы написать реализацию это совсем другое. Но всё равно вы молодцы, у вас еще очень много работы ))
Следуя Вашему совету, попытался расширить пример. Что из этого получилось, решать Вам.
Как часто планируется публиковать описание новых паттернов?
И когда по плану цикл статей будет закончен?
Курс на 2 семестра. Соответственно, практически через год я планирую закончить этот цикл статей. Если конечно не закончу ранее по какой-либо причине.

Новые паттерны, по просьбам трудящихся, будут появляться с периодичностью раз в 2 дня. По крайней мере я буду стараться придерживаться этого цикла.
Утечка, забыли удалить alarm в main().
Да и виртуального деструктора нету ни в одном из классов =)
Согласен :) Просто я по большому счету Java программист. Этот код, что я написал на C++, чуть ли не самое серьезное и реально работающее, что мне доводилось писать на плюсах.

Можно вопрос: А почему он должен быть именно виртуальным? Ведь, в таком случае его надо переопределять каждый раз?
Нет, в C++ переопределять каждый раз надо абстрактные функции. Виртуальные переопределять можно, но не обязательно.

А виртуальный деструктор нужен для того, чтобы, при удалении объекта по указателю на базовый класс вызвался не только деструктор базового класса, но и деструктор производного (и не произошло утечки памяти). Если деструктор не виртуальный, то этого не произойдет.
Только я не пойму, в чем разница между абстрактными и виртуальными функциями, на уровне синтаксиса C++?
В С++:
virtual ReturnType VirtualFunc(ParamType param); — виртуальная функция (можно переопределить в производном классе).
virtual ReturnType PureVirtualFunc(ParamType param) = 0; — чисто виртуальная функция (нужно переопределять в производном классе).
как же я ненавижу using namespace… неужели так сложно написать std::cout?
а как же string? Я использую его. Каждый раз писать std::string? Извольте :) Мне лениво.
using std::string; и все довольны
Поправьте пожалуйста в самом начале «говориться». Что делает? — говорится. В предложении «Как говориться, двух зайцев…».
А так спасибо за рассказ. Интересно было прочесть. К тому же сам я в паттернах пока не очень.
Исправил, спасибо.
Еще после вводного слова «скажем» нужна запятая.
Там, если честно, ещё много ошибок. Извини, но С++ ты знаешь лучше, чем русский.
Хоть на этом спасибо :)
Замечательно!!! +1
Конкретный пример и удобоваримое описание!!!
Похоже на рэп программистов:

Теперь мы можем совершенно независимо
модифицировать абстракцию — путем уточнения
интерфейса абстракции и реализацию —
путем имплементации интерфейса реализации.
Если я все правильно понял, Вам нужно следующее: чтобы будильник мог выполнять различные действия, когда «будит»?

Тогда зачем это все нужно, если можно просто параметризовать абстрактный тип данных «будильник» функцией «будить»?

Будильник представим таким абстрактным типом данных (в ML-like записи):

mk_alarmclock: (wakeup: time -> unit) -> alclock
start: alclock -> unit
stop: alclock -> unit

Вот и все. Чтобы сконструировать будильник (alclock), нужно передать ему функцию wakeup, которая принимает «время» и возвращает unit. И еще доступны две функции, start и stop.

В Си++ Вы скорее всего захотите использовать функтор вместо функции, чтобы хранить все, что Вам нужно. К сожалению, я не полностью понял проблему, которую Вы пытаетесь решить.
Дело в том, что Вы «пытаетесь понять проблему, которую я пытаюсь решить». На самом деле никакой проблемы и нет. Мне не нужен был будильник, выполняющий различные действия на вызов метода «будить». Мне нужно было изучить паттерн и рассказать про него. Для наглядности я придумал пример с будильником.

В реальной ситуации, если мне действительно понадобился бы будильник, я бы написал его используя классический подход абстракция -> реализация. Не более того. Боже упоси использовать при этом функтор :)
Когда, много лет назад возникла подобная задача, сел часам к 24-00 писать резидента на асме пищащего через спикер. Дописал как раз к моменту выхода в школу. Т.е. можно сказать что метод сработал в итоге — «не проспал»:)
Sleep(100);

Опять поллинг. Можно ведь точно посчитать, сколько осталось до звонка и спать именно это время. Текущая реализация будет регулярно требовать к себе внимания, а значит никогда не ляжет спокойно и целиком в виртуальную память. Если в это время компьютер будет делать что-то ресурсоёмкое, текущая реализация будет регулярно свопиться и подниматься из свопа назад, усугубляя положение дел. А если помножить на общее количество всякой мишуры, которая висит в трее и регулярно чего-нибудь опрашивает из-за лени программистов… А потом все жалуются, что система тормозит и память жрёт.
А можно столько ждать? Какой-нибудь watchdog не срубит такую программу?
Слипы на длительные периоды чреваты ( в любой ОС ).

Где-то умный таск-менеджер решит, что программа зависла, где-то сработает синхронизация с NTP-сервером, от чего время в системе скакнет на ± пару минут туда-сюда, где-то произойдет переход на летнее\зимнее время, где-то юзер решит сменить часовой пояс. Во всех этих случаях программа с длительным слипом сработает некорректно. А программа, периодически просыпающаяся и оценивающая текущую окружающую реальность — сработает верно.
прекрасный пример того, как простая задача на 2 строки становится монстром, когда ее пытаются решать при помощи паттернов.
Думал, что это очевидно, но видимо требует пояснения. Задача была выбрана простая, чтобы на практике показать как использовать паттерн. Ценность же использования этого паттерна окажется очень большей в сложных проектах в больших командах.
При всем уважении, у Вас получилось плохо. Вы выбрали задачу, которая не требует паттернов, поэтому Ваш пример скорее отпугнет, чем научит. Понятно конечно, что Вы сейчас учите паттерны, и как писал Твен «To a man with a hammer, everything looks like a nail»
При всем уважении, не я писал статью и не я выбирал задачу. А паттерны я учил давно и имею опыт успешного применения в больших и маленьких проектах.
Товарищи, изобретшие ООП, паттерны, и прочие хрени работали в команде, а не в одного, когда это всё можно по-своему сделать и как угодно.

Здесь предполагается, что минимум 2 человека участвуют в реале: один, кто юзает интерфейс и второй, кто его пишет.
отвечаю Вам словами Paul Graham:

This practice is not only common, but institutionalized. For example, in the OO world you hear a good deal about «patterns». I wonder if these patterns are not sometimes evidence of case ©, the human compiler, at work. When I see patterns in my programs, I consider it a sign of trouble. The shape of a program should reflect only the problem it needs to solve. Any other regularity in the code is a sign, to me at least, that I'm using abstractions that aren't powerful enough, often that I'm generating by hand the expansions of some macro that I need to write.
:)

Вы ещё скиньте сюда нытьё товарищей, которые говорят, что ООП — это зло.

Или, глупости зеленых товарищей о том, что гамбургеры — зло, потому-что хрюшки тоже жить хотят.
это вопрос авторитетов. Вам больше нравится GoF, мне — Dijkstra
Sign up to leave a comment.

Articles