Проектирование гибкого ООП-кода на примере

Проектирование гибкого ООП-кода на примере

Вопросами ООП-проектирования и гибкости программного кода мы уже занимались. В этот раз поработаем над более практичным примером.

Допустим, в приложение требуется добавить эффект мигания текста для QLabel. Подойти к решению задачи можно по разному. Займемся анализом.

В статье QTimer: Примеры использования я упоминал возможность применения QTimer для достижения нужной нам цели. Но куда его вставить?

Неудачные варианты

QTimer можно поместить в виджет, который содержит QLabel. Проблема: если та же возможность понадобится в другом виджете, то мы в пролете. Не подходит.

Можно создать наследник класса QLabel. Проблема: мы окажемся привязаны к QLabel. Если такой же эффект понадобится в другом месте, то мы ничего не сможем сделать. Не подходит.

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

Proxy-класс доступа к текстовым данным

Для доступа к текстовым данным заведем интерфейс TextProxy:

В него можно будет завернуть все, что угодно, а не только QLabel. В качестве первого шага создадим шаблонную реализацию этого интерфейса в расчете на QLabel:

Класс текстового эффекта

Нет смысла ограничиваться лишь эффектом мигания текста. Нам могут понадобиться и другие. Заведем следующий интерфейсный класс:

В нем имеется сигнал с запросом о необходимости обновления и функция-член update(), которая и должна реализовать текстовый эффект.

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

Наконец мы можем добавить реализацию эффекта мигания текста:

Мигание осуществляется за счет попеременного вывода в TextProxy пустой строки и исходного текстового значения. Для этого нам приходится хранить состояние в хэш-карте, ключом которой является указатель на TextProxy.

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

Связующий класс TextAnimator

Добавим небольшой класс, который упростит привязку эффекта к прокси-объекту:

Испытаем его на QLabel, как и хотели:

Все работает. Поэтому теперь проверим получившуюся архитектуру на гибкость.

Эффект бегущей строки

Создадим эффект бегущей строки:

В целом идея реализации напоминает класс BlinkTextEffect. Но есть и свои особенности. Эффект бегущей строки характеризуется максимальным количеством символов, которые мы хотим отображать в каждый момент времени. За это отвечает параметр maxCharWidth. Отрицательные значения для него означают, что нужно отображать всю строку целиком. В этом случае на каждом шаге просто перемещаем первый символ в конец строки:

Чтобы использовать новый эффект вместо старого, достаточно поменять всего одну строку:

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

Proxy для заголовка виджета

Если заголовок слишком длинный, то наш эффект бегущей строки придется весьма кстати. Добавим обертку, которая поможет нам в этом:

А теперь испытаем ее:

И снова все работает без особых проблем.

Выводы

В результате мы пришли к вполне гибкой и пригодной для расширения ООП-архитектуре. Но ее еще можно улучшать. Остается два основных недостатка:

  1. TextEffect рассчитан на работу со статическими текстовыми полями. Любые изменения в обернутом с помощью TextProxy объекте после запуска анимации будут игнорироваться;
  2. Имеется дублирование логики хранения состояния в хэш-картах. А ведь мы разработали всего два эффекта. Логично предположить, что такая же возможность понадобится и в других.

Решение заключается в том, чтобы доработать класс TextProxy. Первую проблему можно устранить, если сделать допущение, что любые изменения текста обернутого объекта должны вноситься только через его proxy-объект.

Второй недостаток устраняется путем переноса карты в сам TextProxy. Поскольку разным эффектам требуется хранить разные данные, то для этого лучше использовать QVariant.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий