Об'єктно-орієнтоване програмування: геттери / сетери або логічні назви


12

Зараз я думаю про інтерфейс до класу, про який пишу. Цей клас містить стилі для символу, наприклад, чи є жирний шрифт, курсив, підкреслений і т. Д. Я два дні дискутував із собою, чи слід використовувати геттери / сеттери або логічні назви для методів, які змінюють значення на ці стилі. Хоча я, як правило, віддаю перевагу логічним іменам, це означає, що написання коду не є настільки ефективним і не таким логічним. Дозвольте навести вам приклад.

У мене є клас , CharacterStylesякий має змінні член bold, italic, underline(і деякі інші, але я залишу їх для простоти). Найпростіший спосіб дозволити іншим частинам програми отримати доступ до цих змінних - написати методи getter / setter, щоб ви могли це робити styles.setBold(true)і styles.setItalic(false).

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

Однак є одна проблема. Оскільки ви не можете отримати доступ до змінної об'єкта-члена за вмістом рядка в C ++, мені доведеться або написати великий контейнер if-statement / switch для всіх стилів, або мені доведеться зберігати стилі в асоціативному масиві ( карта).

Я не можу зрозуміти, який найкращий спосіб. Одного моменту я думаю, що мені слід написати гетерів / сеттерів, а наступного моменту схиляюся в інший бік. Моє запитання: що б ти робив? І чому б ти це робив?


Чи насправді клас CharacterStyles робить щось інше, ніж оснащення трьох булевих? Як воно споживається?
Марк Канлас

Так, тому що символи стилів повинні мати можливість успадковувати інші символи стилів. Це означає, що якщо у мене не визначено CharacterStyles із boldвстановленими значеннями trueта інші змінні, методи getter для інших змінних повинні повернути значення батьківського стилю (який зберігається в іншій властивості), тоді як getter для boldвластивості повинен повернутися true. Є ще деякі речі, такі як ім'я та спосіб його відображення в інтерфейсі.
Жаба

Ви можете використовувати enum, щоб визначити різні параметри стилів, а потім використовувати оператор switch. До речі, як ти поводишся з невизначеним?
Тіанна

@Tyanna: Дійсно, я також думав про те, щоб використати enum, але це лише незначна деталь. Я планував просто встановити невизначені значення NULL. Це не найкращий спосіб?
Жаба

Відповіді:


6

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

Тепер, якщо ви хочете отримати більш складні методи доступу до поля, це дійсно, але замість того, щоб виставляти поле, вам потрібно подумати, які методи повинен запропонувати клас. тобто. замість класу банку зі значенням Money, яке піддається впливу властивості, вам потрібно подумати, які типи доступу має запропонувати об’єкт Банку (додати гроші, зняти гроші, отримати баланс) та застосувати ці замість цього. Властивість змінної Money лише синтаксично відрізняється від експонування змінної Money безпосередньо.

У DrDobbs є стаття, в якій сказано більше.

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

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


Відмінна відповідь! Особливо частина про збереження даних, оскільки HTML спричинив мене як хороший привід дійсно піти по цій дорозі. Тож ви б запропонували зберігати різні стилі в перерахунку, а потім встановлювати такі стилі, як styles.setStyle(BOLD, true)? Це правильно?
Жаба

так, у мене тільки два способи - setStyle (BOLD) та clearstyle (BOLD). забудьте другий параметр, я просто віддаю перевагу такому, і ви можете потім перевантажувати clearStyle, щоб не брати жодних параметрів, щоб очистити всі стилі прапорів.
gbjbaanb

Це не працює, тому що для деяких типів (наприклад, розмір шрифту) потрібен параметр. Але все ж дякую за відповідь!
Жаба

Я намагався реалізувати це, але є одна проблема, яку я не врахував: як ти реалізуєш, styles.getStyle(BOLD)коли ти не маєш лише змінних членів типу булева, але і типів цілих і рядкових (наприклад styles.getStyle(FONTSIZE):). Оскільки ви не можете перевантажувати типи повернення, як це програмувати? Я знаю, що ти можеш використовувати пусті покажчики, але це здається мені дуже поганим способом. Чи є у вас підказки, як це зробити?
Жаба

ви можете повернути об'єднання, структуру, або з новим стандартом ви можете перевантажуватись за типом повернення. Це означає, що ви намагаєтесь реалізувати єдиний метод для встановлення стилю, де стиль - це прапор, і сім'я шрифтів, і розмір? Можливо, ви намагаєтеся в цьому випадку занадто сильно змусити абстракцію.
gbjbaanb

11

Одна з ідей, яку ви, можливо, не врахували, - це візерунок декораторів . Замість того, щоб встановлювати прапори в об’єкт, а потім застосовувати ці прапори до всього, що ви пишете, ви загортаєте клас, який робить написання, в «Декоратори», які, в свою чергу, застосовують стилі.

Коду виклику не потрібно знати, скільки цих обгортків ви помістили навколо тексту, ви просто зателефонуєте методу на зовнішній об'єкт, і він викличе стек.

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

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()));
}

І код, який викликає цей метод, не повинен знати деталі того, що він отримує. Поки що SOMETHING може малювати текст методом WriteString.


Гммм, я збираюсь поглянути на це завтра свіжим розумом і повернуся до тебе тоді. Дякую за відповідь.
Жаба

Я великий шанувальник візерунка «Декоратор». Той факт, що ця модель нагадує HTML (де основна операція полягає в укладанні рядка введення тегами), є свідченням того, що такий підхід може задовольнити всі потреби користувача вашого інтерфейсу. Слід пам’ятати, що хороший і простий у користуванні інтерфейс може мати базову реалізацію, яка є технічно складною. Наприклад, якщо ви реально реалізуєте текстовий візуалізатор самостійно, вам може виявитися, що вам TextWriterпотрібно поговорити з обома BoldDecoratorі ItalicDecorator(двосторонньо), щоб виконати роботу.
rwong

Хоча це гарна відповідь, хіба це не вводить повторно питання ОП? "сміливий аплікатор" -> як це робити? використовуючи сетери / геттери на зразок SetBold ()? Яке саме питання ОП.
стихій

1
@stijn: Не дуже. Існує ряд способів уникнути такої ситуації. Наприклад, ви можете зафіксувати це в будівельнику методом toggleStyle (аргумент enum), який додає та видаляє декоратори з масиву, лише прикрашаючи остаточний елемент, коли ви викликаєте BuildDecoratedTextWriter. Або ви можете зробити так, як запропонував rwong, і ускладнити базовий клас. Залежить від обставини, і ОП не була достатньо конкретна щодо загальної специфікації, щоб здогадатися, що найкраще для нього. Він здається досить розумним, щоб можна було це зрозуміти, як тільки він отримає шаблон.
пдр

1
Я завжди думав, що візерунок декораторів передбачає замовлення прикрас. Розглянемо показаний приклад коду; як можна піти про видалення ItalicDecorator? Тим не менш, я думаю, що щось на зразок декоратора - це шлях. Жирний, курсив і підкреслення - не єдині три стилі. Там є тонкий, напівжирний, згущений, чорний, широкий, курсив із шліфом, маленькі ковпачки тощо. Можливо, вам знадобиться спосіб застосувати до персонажа довільно великий набір стилів.
Баррі Браун

0

Я схиляюся до другого рішення. Це виглядає більш елегантно і гнучко. Хоча важко уявити інші типи, крім жирного, курсивного та підкреслення (перекреслення?), Важко буде додати нові типи за допомогою змінних членів.

Я використовував такий підхід в одному з моїх останніх проектів. У мене клас, який може мати кілька булевих атрибутів. Кількість та назви атрибутів можуть змінюватися в часі. Я зберігаю їх у словнику. Якщо атрибута немає, я припускаю, що його значення "хибне". Я також повинен зберігати список доступних імен атрибутів, але це вже інша історія.


1
Окрім цих типів, я б потім додав перекреслені, розміри шрифтів, сімейство шрифтів, суперскрипт, індекс та, можливо, інші. Але чому ви вибрали саме цей метод у своєму проекті? Ви не вважаєте, що брудний код повинен зберігати список дозволених атрибутів? Мені дуже цікаво, які ваші відповіді на ці запитання!
Жаба

Мені довелося зіткнутися з ситуацією, коли деякі атрибути могли бути визначені клієнтами, коли додаток вже було розгорнуто. Для деяких клієнтів були потрібні деякі інші атрибути, але застарілі для інших. Я можу налаштувати форми, збережені набори даних та вирішити інші проблеми, використовуючи цей підхід. Я не думаю, що це генерує брудний код. Іноді ви просто не можете передбачити, яка інформація буде потрібна вашому клієнту.
Анджей Бобак

Але моєму клієнту не потрібно буде додавати спеціальні атрибути. І в такому випадку я думаю, що це виглядає трохи "хакі". Ви не згодні?
Жаба

Якщо ви не стикаєтесь з динамічною кількістю атрибутів, вам не потрібно гнучкий простір для їх зберігання. Це правда :) Я не згоден з іншою частиною. Наголошення атрибутів у словнику / хешмапі / тощо не є поганим підходом.
Анджей Бобак

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.