Объектно-ориентированное программирование: геттеры / сеттеры или логические имена

12

В настоящее время я думаю об интерфейсе для класса, который я пишу. Этот класс содержит стили для символа, например, выделен ли он жирным шрифтом, курсивом, подчеркнут и т. Д. Я уже два дня спорю сам с собой о том, следует ли использовать методы получения / установки или логические имена для методов, которые изменяют значения на эти стили. Хотя я предпочитаю логические имена, это означает написание кода, который не так эффективен и не так логичен. Позвольте привести пример.

У меня есть класс , CharacterStylesкоторый имеет переменные член bold, italic, underline(и некоторые другие, но я оставлю их для простоты). Самый простой способ разрешить другим частям программы доступ к этим переменным - написать методы getter / setter, чтобы вы могли делать styles.setBold(true)и styles.setItalic(false).

Но мне это не нравится. Не только потому, что многие люди говорят, что геттеры / сеттеры нарушают инкапсуляцию (неужели это так плохо?), Но в основном потому, что это не кажется мне логичным. Я ожидаю стилизовать персонажа одним методом styles.format("bold", true)или чем-то подобным, но не всеми этими методами.

Хотя есть одна проблема. Поскольку вы не можете получить доступ к переменной-члену объекта по содержимому строки в C ++, я должен был бы либо написать большой контейнер if-Statement / switch для всех стилей, либо мне пришлось бы хранить стили в ассоциативном массиве ( карта).

Я не могу понять, как лучше. В один момент я думаю, что должен написать геттеры / сеттеры, а в следующий момент я склоняюсь в другую сторону. Мой вопрос: что бы вы сделали? И зачем ты это делаешь?

Лягушка
источник
Действительно ли класс CharacterStyles делает что-то кроме связки трех логических значений? Как это потребляется?
Марк Канлас
Да, потому что CharacterStyles должны иметь возможность наследовать от других CharacterStyles. Это означает, что если у меня есть CharacterStyles с boldустановленным в trueи другими неопределенными переменными, методы получения для других переменных должны возвращать значение родительского стиля (который хранится в другом свойстве), в то время как получение для boldсвойства должно возвращаться true. Есть и другие вещи, такие как имя и способ отображения в интерфейсе.
Лягушка
Вы можете использовать enum для определения различных опций стиля, а затем использовать оператор switch. Кстати, как вы справляетесь с неопределенным?
Тианна
@Tyanna: Действительно, я тоже подумала об использовании enum, но это лишь небольшая деталь. Я планировал просто установить неопределенные значения в NULL. Разве это не лучший способ?
Лягушка

Ответы:

6

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

Теперь, если вам нужны более сложные методы для доступа к полю, это допустимо, но вместо того, чтобы выставлять поле, вам нужно подумать, какие методы класс должен предлагать вместо этого. то есть. вместо класса Bank со значением Money, которое определяется с помощью свойства, вам нужно подумать, какие типы доступа должен предлагать объект Bank (добавить деньги, вывести, получить баланс) и реализовать их вместо этого. Свойство переменной Money только синтаксически отличается от прямого доступа к переменной Money.

У DrDobbs есть статья, которая говорит больше.

Для вашей проблемы у меня было бы 2 метода setStyle и clearStyle (или любой другой), которые принимают перечисление возможных стилей. Оператор switch внутри этих методов затем применяет соответствующие значения к соответствующим переменным класса. Таким образом, вы можете изменить внутреннее представление стилей на что-то другое, если позже решите сохранить их в виде строки (например, для использования в HTML) - то, что потребовало бы изменения всех пользователей вашего класса, если бы вы использовали получить / установить свойства.

Вы все еще можете включить строки, если хотите принять произвольные значения, либо иметь большой оператор if-then (если их несколько), либо сопоставить строковые значения с указателями на методы, используя std :: mem_fun (или std :: функция ), так что «полужирный» будет храниться в ключе карты с его значением, являющимся sts :: mem_fun для метода, который устанавливает переменную полужирный в true (если строки совпадают с именами переменных-членов, то вы также можете использовать stringifying макрос , чтобы уменьшить количество кода , вам нужно написать)

gbjbaanb
источник
Отличный ответ! Особенно часть о хранении данных в виде HTML показалась мне хорошей причиной, чтобы действительно пойти по этому пути. Итак, вы бы предложили сохранить различные стили в перечислении, а затем установить стили как styles.setStyle(BOLD, true)? Это верно?
Лягушка
да, только у меня было бы 2 метода - setStyle (BOLD) и clearstyle (BOLD). забудьте второй параметр, я просто предпочитаю его так, и вы можете затем перегрузить clearStyle, чтобы не принимать никаких параметров, чтобы очистить все флаги стиля.
gbjbaanb
Это не сработает, потому что некоторым типам (например, font-size) нужен параметр. Но все равно спасибо за ответ!
лягушка
Я пытался реализовать это, но есть одна проблема, которую я не рассматривал: как вы реализуете, styles.getStyle(BOLD)когда у вас есть не только переменные-члены типа boolean, но также и типы integer и string (например:) styles.getStyle(FONTSIZE). Поскольку вы не можете перегружать типы возвращаемых данных, как вы программируете это? Я знаю, что вы можете использовать пустые указатели, но для меня это звучит очень плохо. Есть ли у вас какие-либо советы, как это сделать?
лягушка
вы можете вернуть объединение, или структуру, или с новым стандартом, вы можете перегрузить типом возвращаемого значения. Тем не менее, вы пытаетесь реализовать один метод для установки стиля, где стиль - это флаг, семейство шрифтов и размер? Может быть, вы пытаетесь заставить абстракцию слишком много в этом случае.
gbjbaanb
11

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

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

Для примера псевдокода:

class TextWriter : TextDrawingInterface {
    public:
        void WriteString(string x) {
            // write some text somewhere somehow
        }
}

class BoldDecorator : TextDrawingInterface {
    public:
        void WriteString(string x) {
            // bold application on
            m_textWriter.WriteString(x);
            // bold application off
        }

        ctor (TextDrawingInterface textWriter) {
            m_textWriter = textWriter;
        }

    private:
        TextWriter m_TextWriter;
}

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

TextDrawingInterface GetDecoratedTextWriter() {
    return new BoldDecorator(new ItalicDecorator(new TextWriter()));
}

И коду, который вызывает этот метод, не нужно знать детали того, что он получает. Пока что-то может рисовать текст через метод WriteString.

прецизионный самописец
источник
Хммм, я собираюсь взглянуть на это завтра со свежим умом и вернусь к вам тогда. Спасибо за ответ.
Лягушка
Я большой поклонник шаблона Decorator. Тот факт, что этот шаблон напоминает HTML (где основная операция заключается в том, чтобы заключить входную строку с тегами), свидетельствует о том, что этот подход может удовлетворить все потребности пользователя вашего интерфейса. Следует иметь в виду, что интерфейс, который является хорошим и простым в использовании, может иметь базовую реализацию, которая технически сложна. Например, если вы на самом деле внедряете текстовый рендер, вы можете обнаружить, что вам TextWriterнужно поговорить с обоими BoldDecoratorи ItalicDecorator(в двух направлениях), чтобы выполнить работу.
Rwong
в то время как это хороший ответ, разве это не повторно вводит вопрос op? "смелое заявление" -> как это будет сделано? используя сеттеры / геттеры, такие как SetBold ()? Который является точно вопросом оп.
Стейн
1
@stijn: Не совсем. Есть несколько способов избежать этой ситуации. Например, вы можете обернуть это в компоновщике с помощью метода toggleStyle (аргумент enum), который добавляет и удаляет декораторы из массива, декорируя только последний элемент при вызове BuildDecoratedTextWriter. Или вы можете сделать так, как предложил Руонг, и усложнить базовый класс. Зависит от обстоятельств, и ОП не был достаточно конкретным в отношении общей спецификации, чтобы угадать, что лучше для него. Он, кажется, достаточно умен, чтобы иметь возможность понять это, как только получит образец.
фунтовые
1
Я всегда думал, что Образец Декоратора подразумевает заказ украшений. Рассмотрим пример кода, показанный; как можно удалить ItalicDecorator? Тем не менее, я думаю, что-то вроде Decorator это путь. Жирный, курсив и подчеркивание - не единственные три стиля. Это тонкий, полужирный, сжатый, черный, широкий, курсив со штрихом, маленькие заглавные буквы и т. Д. Вам может понадобиться способ применить произвольно большой набор стилей к персонажу.
Барри Браун
0

Я бы склонялся ко второму решению. Это выглядит более элегантно и гибко. Хотя трудно представить другие типы, кроме жирного, курсива и подчеркивания (зачеркнуты?), Было бы трудно добавить новые типы с помощью переменных-членов.

Я использовал этот подход в одном из моих последних проектов. У меня есть класс, который может иметь несколько логических атрибутов. Количество и названия атрибутов могут меняться со временем. Я храню их в словаре. Если атрибут отсутствует, я предполагаю, что его значение равно «false». Я также должен хранить список доступных имен атрибутов, но это уже другая история.

Анджей Бобак
источник
1
Помимо этих типов, я бы добавил зачеркивание, размер шрифта, семейство шрифтов, верхний индекс, нижний индекс и, возможно, другие позже. Но почему вы выбрали этот метод в своем проекте? Не думаете ли вы, что это грязный код - хранить список разрешенных атрибутов? Мне действительно любопытно, каковы ваши ответы на эти вопросы!
Лягушка
Мне пришлось столкнуться с ситуацией, когда некоторые атрибуты могли быть определены клиентами, когда приложение уже было развернуто. Некоторые другие атрибуты были необходимы для некоторых клиентов, но устарели для других. Я мог бы настроить формы, наборы хранимых данных и решить другие проблемы, используя этот подход. Я не думаю, что это генерирует грязный код. Иногда вы просто не можете предсказать, какая информация понадобится вашему клиенту.
Анджей Бобак
Но моему клиенту не нужно добавлять пользовательские атрибуты. И в этом случае я думаю, что это выглядит немного "хаки". Ты не согласен?
Лягушка
Если вы не столкнулись с проблемой динамического количества атрибутов, вам не нужно гибкое пространство для их хранения. Это правда :) Я не согласен с другой частью, хотя. Создание атрибутов в словаре / hashmap / etc, на мой взгляд, неплохой подход.
Анджей Бобак
0

Я думаю, что вы продумываете проблему.

styles.setBold(true) и styles.setItalic(false)все в порядке

Тулаинс Кордова
источник