Разве нет места, где инкапсуляция становится смешной? - PullRequest
11 голосов
/ 11 ноября 2009

Для моего класса программирования по разработке программного обеспечения мы должны были создать программу типа «Менеджер каналов» для каналов 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;
};

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


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

public class Example
{
    private int _myint;

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

Я имею в виду, что я GET зачем они это делают, может позже они захотят проверить данные в установщике или увеличить их в получателе. Но почему вы, люди, просто НЕ ДЕЛАЕТЕ ЭТУ ДО такой ситуации?

public class Example
{
    public int MyInt;
}

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

Ответы [ 13 ]

14 голосов
/ 11 ноября 2009

Это вопрос «лучшей практики» и стиля.

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

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

13 голосов
/ 11 ноября 2009

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

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

7 голосов
/ 11 ноября 2009

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

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

Гораздо безопаснее сделать это заранее.

C # 3, кстати, делает это проще:

public class Example
{
    public int MyInt { get; set; }
}
4 голосов
/ 11 ноября 2009

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

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

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

4 голосов
/ 11 ноября 2009

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

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

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

3 голосов
/ 12 ноября 2009

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

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

Так что канонический C # способ сделать то, что вы делаете, будет выглядеть так:

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" };

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

3 голосов
/ 11 ноября 2009

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

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

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

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

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

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

1 голос
/ 23 октября 2010

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

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

1 голос
/ 11 ноября 2009

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

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

1 голос
/ 11 ноября 2009

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

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

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

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

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

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

...