Разве нет места, где инкапсуляция становится смешной?

Для моего класса программирования разработки программного обеспечения мы должны были создать программу типа "Feed Manager" для RSS-каналов. Вот как я обработал реализацию FeedItems.

Приятный и простой:

struct FeedItem {
    string title;
    string description;
    string url;
}

Я получил обозначение для этого, "правильный" пример ответа выглядит следующим образом:

class FeedItem
{
public:
    FeedItem(string title, string description, string url);

    inline string getTitle() const { return this->title; }
    inline string getDescription() const { return this->description; }
    inline string getURL() const { return this->url; }

    inline void setTitle(string title) { this->title = title; }
    inline void setDescription(string description){ this->description = description; }
    inline void setURL(string url) { this->url = url; }

private:
    string title;
    string description;
    string url;
};

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


Это напоминает мне, как в С# люди всегда это делают:

public class Example
{
    private int _myint;

    public int MyInt
    {
        get
        {
            return this._myint;
        }
        set
        {
            this._myint = value;
        }
    }
}

Я имею в виду, что я ПОЛУЧАЮ, почему они это делают, возможно, позже они захотят проверить данные в сеттере или увеличить его в получателе. Но почему бы вам просто не сделать ЭТО, если эта ситуация возникнет?

public class Example
{
    public int MyInt;
}

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

Ответ 1

Это проблема "лучшей практики" и стиля.

  • Вы никогда не захотите напрямую открывать свои данные. Вы всегда хотите иметь возможность контролировать доступ к ним. Я согласен, в этом случае это кажется немного смешным, но он предназначен для того, чтобы научить вас этому стилю, чтобы вы привыкли к нему.
  • Это помогает определить согласованный интерфейс для классов. Вы всегда знаете, как добраться до чего-то → вызываете его метод get.

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

Ответ 2

Здесь хорошая длинная дискуссия SO по теме: Зачем использовать геттеры и сеттеры.

Вопрос, который вы хотите задать себе: "Что произойдет через 3 месяца, когда вы поймете, что FeedItem.url нужно подтвердить, но он уже ссылался непосредственно на 287 других классов?"

Ответ 3

Основная причина сделать это до его необходимости - для управления версиями.

Поля ведут себя иначе, чем свойства, особенно при использовании их как lvalue (где это часто не допускается, особенно в С#). Кроме того, если вам нужно, позднее, добавить процедуры get/set свойств, вы нарушите свой API - пользователям вашего класса потребуется переписать их код, чтобы использовать новую версию.

Это гораздо безопаснее сделать это впереди.

С# 3, btw, делает это проще:

public class Example
{
    public int MyInt { get; set; }
}

Ответ 4

Я абсолютно согласен с тобой. Но в жизни вы, вероятно, должны делать "Правильную вещь": в школе, чтобы получить хорошие оценки. На вашем рабочем месте это должно соответствовать спецификациям. Если вы хотите быть упрямым, то это хорошо, но объясните сами, - закройте свои базы комментариями, чтобы свести к минимуму ущерб, который вы можете получить.

В вашем конкретном примере выше я вижу, что вы можете проверить, скажем, URL-адрес. Может быть, вы даже захотите дезинформировать название и описание, но в любом случае я думаю, что это то, о чем вы можете рассказать на раннем этапе в дизайне класса. Укажите свои намерения и ваше обоснование в комментариях. Если вам не нужна проверка, вам не нужен геттер и сеттер, вы абсолютно правы.

Простота платит, это ценная функция. Никогда не делайте ничего религиозного.

Ответ 5

Если что-то простая структура, то да это смешно, потому что это просто DATA.

Это действительно просто возврат к началу ООП, где люди по-прежнему вообще не понимают классов. Нет причин, чтобы сотни методов get и set были на всякий случай, если вы однажды измените getId() на удаленный вызов телескопа хаббла.

Вам действительно нужна эта функциональность на уровне TOP, внизу она бесполезна. IE у вас будет сложный метод, которому был отправлен чистый виртуальный класс для работы, гарантируя, что он все равно может работать независимо от того, что происходит ниже. Просто произвольное размещение в каждой структуре - это шутка, и это никогда не должно быть сделано для POD.

Ответ 6

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

См. http://www.pragprog.com/articles/tell-dont-ask

Теперь представим, что ваш класс FeedItem стал чрезвычайно популярным и используется проектами повсюду. Вы решили, что вам нужно (как предложили другие ответы) подтвердить URL, который был предоставлен.

Счастливые дни, вы написали сеттер для URL. Вы редактируете это, проверяете URL-адрес и генерируете исключение, если оно недействительно. Вы выпускаете новую версию этого класса, и каждый, кто ее использует, счастлив. (Пусть проигнорировано проверено vs unchecked исключений, чтобы сохранить этот трек).

Кроме того, вы получаете звонок от сердитого разработчика. Они читали список фидтиментов из файла, когда их приложение запускается. И теперь, если кто-то совершит небольшую ошибку в файле конфигурации, вы получите новое исключение, и вся система не запустится, просто потому, что один фид фидов был неправильным!

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

Ответ 7

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

Например, в С# концепция геттера/сеттера была испечена на языке в виде свойств. С# 3.0 упростил внедрение автоматических свойств, когда компилятор автоматически генерирует для вас геттер/сеттер. С# 3.0 также вводил инициализаторы объектов, что означает, что в большинстве случаев вам больше не нужно объявлять конструкторы, которые просто инициализируют свойства.

Таким образом, канонический способ С# для выполнения того, что вы делаете, будет выглядеть следующим образом:

class FeedItem
{
    public string Title { get; set; } // automatic properties
    public string Description { get; set; }
    public string Url { get; set; }
};

И использование будет выглядеть так (с использованием инициализатора объекта):

FeedItem fi = new FeedItem() { Title = "Some Title", Description = "Some Description", Url = "Some Url" };

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

Ответ 8

Как разработчик С++, я делаю своих членов всегда приватными, просто чтобы быть последовательными. Поэтому я всегда знаю, что мне нужно ввести p.x(), а не p.x.

Кроме того, я обычно избегаю применения методов setter. Вместо изменения объекта я создаю новый:

p = Point(p.x(), p.y() + 1);

Это также сохраняет инкапсуляцию.

Ответ 9

Абсолютно существует точка, где инкапсуляция становится смешной.

Чем больше абстракций вводится в код, тем выше будет ваше начальное образование, стоимость обучения-кривой будет.

Каждый, кто знает C, может отлаживать ужасно написанную 1000-строчную функцию, которая использует только базовую библиотеку языка C. Не все могут отлаживать разработанную вами инфраструктуру. Каждая внедренная инкапсуляция/абстракция уровня должна быть сопоставлена ​​с затратами. Это не значит, что это не стоит того, но, как всегда, вы должны найти оптимальный баланс для своей ситуации.

Ответ 10

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

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

Инкапсуляция была предложена как метод обеспечения того, чтобы созданные модули были повторно использованы. То есть есть четко определенный интерфейс, который абстрагирует детали модуля и облегчает использование этого модуля позже. Интерфейс также предотвращает неправильное использование объекта.

Простые классы, которые вы создаете в классе, недостаточно иллюстрируют необходимость в хорошо определенном интерфейсе. Высказывание "Но почему вы не делаете этого только ЭТО, если эта ситуация возникает?" не будет работать в реальной жизни. То, что вы изучаете в своем курсе по разработке программного обеспечения, - это разработка программного обеспечения, которое смогут использовать другие программисты. Учтите, что создатели библиотек, например, предоставляемые инфраструктурой .net и Java API, абсолютно нуждаются в этой дисциплине. Если бы они решили, что инкапсуляция была слишком большой проблемой, с этими средами было бы почти невозможно работать.

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

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

Ответ 11

Getters/Setters - это, конечно, хорошая практика, но они утомительны, чтобы писать и, что еще хуже, читать.

Сколько раз мы читали класс с полдюжиной переменными-членами и сопутствующими геттерами/сеттерами, каждый из которых имел полный hog @param/@return HTML-кодированный, лихой бесполезный комментарий, такой как "получить значение X", "установить значение X ',' получить значение Y ',' установить значение Y ',' получить значение Z ',' установить значение Zzzzzzzzzzzzz. бухать!

Ответ 12

Это очень распространенный вопрос: "Но почему вы просто не делаете ЭТО ЭТО, если эта ситуация возникает?". Причина проста: обычно гораздо дешевле не исправлять/повторять/передислоцировать ее позже, но сделать это в первый раз. По старым оценкам, расходы на техническое обслуживание составляют 80%, и большая часть этого обслуживания - именно то, что вы предлагаете: делать правильные вещи только после того, как у кого-то возникла проблема. Выполнение этого в первый раз позволяет нам сосредоточиться на более интересных вещах и быть более продуктивными.

Небрежное кодирование обычно очень невыгодно - ваши клиенты недовольны, потому что продукт ненадежен, и они неэффективны, когда они его используют. Разработчики тоже недовольны - они тратят 80% времени на исправления, что скучно. В конце концов вы можете потерять как клиентов, так и хороших разработчиков.

Ответ 13

Я согласен с вами, но важно выжить в системе. Пока в школе, притворяйтесь, что согласитесь. Другими словами, то, что вы отметили, наносит ущерб вам, и вам не стоит отмечать ваши принципы, мнения или ценности.

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

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