Меня интересует хороший объектно-ориентированный дизайн классов. В частности, мне трудно выбирать между этими вариантами:
- статический метод против экземпляра
- метод без параметров или возвращаемого значения против метода с параметрами и возвращаемым значением
- перекрывающиеся и отличные функциональные возможности метода
- приват против публичного метода
Пример 1:
Эта реализация использует методы экземпляра, без возвращаемого значения или параметров, без перекрывающихся функций, и все методы общедоступны.
XmlReader reader = new XmlReader(url);
reader.openUrl();
reader.readXml();
Document result = reader.getDocument();
Пример 2:
В этой реализации используются статические методы с возвращаемыми значениями и параметрами, с перекрывающимися функциями и закрытыми методами.
Document result = XmlReader.readXml(url);
В первом примере все методы являются публичными экземплярами, что упрощает их модульное тестирование. Хотя все методы различны, readXml () зависит от openUrl (), в этом openUrl () должен быть вызван первым. Все данные объявляются в полях экземпляра, поэтому нет никаких возвращаемых значений или параметров ни в одном методе, кроме как в конструкторе и методах доступа.
Во втором примере только один метод является общедоступным, остальные являются закрытыми статическими, что затрудняет их модульное тестирование. Методы перекрываются в том, что readXml () вызывает openUrl (). Там нет полей, все данные передаются как параметры в методах, и результат возвращается немедленно.
Каким принципам я должен следовать, чтобы делать правильное объектно-ориентированное программирование?
источник
Ответы:
Пример 2 довольно плох для тестирования ... и я не имею в виду, что вы не можете проверить внутренности. Вы также не можете заменить свой
XmlReader
объект на фиктивный объект, так как у вас его вообще нет.Пример 1 излишне сложен в использовании. Как насчет
который не сложнее в использовании, чем ваш статический метод.
Такие вещи, как открытие URL, чтение XML, преобразование байтов в строки, синтаксический анализ, закрытие сокетов и т. Д., Неинтересны. Создание объекта и его использование важно.
Так что ИМХО правильный ОО-Дизайн должен сделать общедоступными только две вещи (если вам не нужны промежуточные шаги по какой-то причине). Статика это зло.
источник
Document result = new XmlReader(url).getDocument();
Почему? Так что я могу обновить егоDocument result = whateverReader.getDocument();
иwhateverReader
вручил мне что-то еще.В том-то и дело, что нет единственно правильного ответа и нет абсолютного определения «правильного объектно-ориентированного проектирования» (некоторые люди предложат вам один, но они наивны ... дают им время).
Все сводится к вашим целям.
Вы художник, а бумага пуста. Вы можете нарисовать нежный, мелко нарисованный черно-белый портрет или абстрактную картину с огромными пучками смешанных неонов. Или что-нибудь промежуточное.
Итак, что ПРАВДА для проблемы, которую вы решаете? Каковы жалобы людей, которым нужно использовать ваши классы для работы с XML? Что сложного в их работе? Какой код они пытаются написать, который окружает вызовы вашей библиотеки, и как вы можете помочь этому потоку лучше для них?
Хотели бы они больше краткости? Хотели бы они, чтобы он был очень умен при вычислении значений по умолчанию для параметров, чтобы им не приходилось указывать много (или что-то еще), и он угадывает правильно? Можете ли вы автоматизировать задачи настройки и очистки, которые требуются вашей библиотеке, чтобы они не могли забыть эти шаги? Что еще вы можете сделать для них?
Черт, то, что вам, вероятно, нужно сделать, это написать 4 или 5 различных способов, затем надеть шляпу потребителя и написать код, который использует все 5, и посмотреть, что лучше. Если вы не можете сделать это для всей вашей библиотеки, то сделайте это для подмножества. И вам нужно добавить некоторые дополнительные альтернативы в свой список - как насчет свободного интерфейса, или более функционального подхода, или именованных параметров, или чего-то, основанного на DynamicObject, чтобы вы могли составить осмысленные «псевдо-методы», которые помогут им вне?
Почему jQuery король прямо сейчас? Потому что Resig и команда следовали этому процессу, пока не столкнулись с синтаксическим принципом, который невероятно уменьшил количество кода JS, необходимого для работы с dom и событиями. Этот синтаксический принцип не был понятен им или кому-либо еще, когда они начали. Они НАШЛИ это.
Как программист, это то, что ваше высшее призвание. Ты нащупываешься в темноте, пытаясь что-то делать, пока не найдешь. Когда вы это сделаете, вы будете знать. И вы дадите своим пользователям огромный скачок производительности. И это то, что дизайн (в области программного обеспечения) это все о.
источник
Второй вариант лучше, так как его проще использовать людям (даже если это только вы), гораздо проще.
Для модульного тестирования я бы просто протестировал интерфейс, а не внутренние компоненты, если вы действительно хотите затем переместить внутренние компоненты из частного в защищенный.
источник
1. Статика против экземпляра
Я думаю, что есть очень четкие рекомендации о том, что такое хороший ОО дизайн, а что нет. Проблема в том, что блогосфера затрудняет отделение хорошего от плохого и безобразного. Вы можете найти какой- то справочник, поддерживающий даже худшую практику, о которой вы только можете подумать
И худшая практика, о которой я могу подумать, - это Global State, включая упомянутую вами статику и любимый всеми синглтон. Некоторые выдержки из классической статьи Миско Хевери на эту тему .
Это сводится к тому, что вы не должны предоставлять статические ссылки на все, что имеет какое-то сохраненное состояние. Единственное место, где я использую статику - это перечисляемые константы, и у меня есть опасения даже по этому поводу.
2. Методы с входными параметрами и возвращаемыми значениями по сравнению с методами без
Вы должны понять, что методы, которые не имеют входных параметров и выходных параметров, в значительной степени гарантированно будут работать в каком-то внутреннем состоянии (иначе, что они делают?). Есть целые языки , которые построены на идее избегания сохраненного состояния.
Каждый раз, когда вы сохраняете состояние, у вас есть возможность побочных эффектов, поэтому убедитесь, что вы всегда используете его внимательно. Это подразумевает, что вам следует отдавать предпочтение функциям с определенными входами и / или выходами.
И, на самом деле, функции, которые определили входы и выходы, гораздо проще протестировать - вам не нужно запускать здесь функцию и смотреть туда, чтобы увидеть, что произошло, и вам не нужно где-то устанавливать свойство иначе, прежде чем запускать тестируемую функцию.
Вы также можете безопасно использовать этот тип функции в качестве статики. Однако я бы этого не сделал, потому что, если бы потом мне захотелось где-то использовать немного другую реализацию этой функции, вместо того, чтобы предоставить другой экземпляр новой реализации, я застрял без возможности заменить функциональность.
3. Перекрывающиеся против Различных
Я не понимаю вопроса. Каким будет преимущество в двух перекрывающихся методах?
4. Частное против публичного
Не выставляйте ничего, что вам не нужно подвергать. Тем не менее, я не большой поклонник частного, либо. Я не разработчик C #, но разработчик ActionScript. Я потратил много времени на код Adobe Flex Framework, который был написан около 2007 года. И они сделали очень плохой выбор того, что делать приватным, что превращает их в кошмар, пытаясь расширить свои классы.
Таким образом, если вы не считаете себя лучшим архитектором, чем разработчики Adobe примерно в 2007 году (из вашего вопроса я бы сказал, что у вас есть еще несколько лет, прежде чем у вас появится возможность заявить об этом), вы, вероятно, захотите просто по умолчанию использовать защищенный ,
Есть некоторые проблемы с вашими примерами кода, которые означают, что они плохо спроектированы, поэтому невозможно выбрать A или B.
Во-первых, вы, вероятно, должны отделить создание вашего объекта от его использования . Таким образом, вы обычно не имеете
new XMLReader()
права рядом с тем, где оно используется.Кроме того, как говорит @djna, вы должны инкапсулировать методы, используемые в вашем XML Reader, так что ваш API (пример экземпляра) может быть упрощен до:
Я не знаю, как работает C #, но, поскольку я работал с рядом веб-технологий, я подозреваю, что вы не всегда сможете сразу вернуть XML-документ (за исключением, возможно, в качестве обещания или будущего типа). объект), но я не могу дать вам совет о том, как обрабатывать асинхронную загрузку в C #.
Обратите внимание, что при таком подходе вы можете создать несколько реализаций, которые могут принимать параметр, который сообщает им, где / что читать и возвращать объект XML, и менять их в зависимости от потребностей вашего проекта. Например, вы можете читать напрямую из базы данных, из локального хранилища или, как в исходном примере, с URL-адреса. Вы не можете сделать это, если вы используете статический метод.
источник
Сосредоточьтесь на перспективе клиента.
Статические методы имеют тенденцию ограничивать будущее развитие, наш клиент работает с точки зрения интерфейса, предоставляемого, возможно, многими различными реализациями.
Основная проблема с вашей идиомой открытия / чтения заключается в том, что клиент должен знать порядок, в котором следует вызывать методы, когда он хочет только выполнить простую работу. Здесь очевидно, но в большем классе это далеко не очевидно.
Основной метод для проверки - read (). Внутренние методы можно сделать видимыми для тестовых программ, сделав их ни общедоступными, ни закрытыми, и поместив тесты в один и тот же пакет - тесты по-прежнему можно хранить отдельно от выпущенного кода.
источник
На практике вы обнаружите, что статические методы обычно ограничены служебными классами и не должны загромождать ваши доменные объекты, менеджеров, контроллеров или DAO. Статические методы наиболее полезны, когда все необходимые ссылки могут быть разумно переданы в качестве параметров и предоставляют некоторые функциональные возможности, которые можно повторно использовать во многих классах. Если вы обнаружите, что используете статический метод в качестве обходного пути для ссылки на объект экземпляра, спросите себя, почему вместо этого у вас просто нет этой ссылки.
Если вам не нужны параметры метода, не добавляйте их. То же самое относится и к возвращаемому значению. Помня об этом, вы упростите свой код и убедитесь, что вы не программируете для множества сценариев, которые никогда не заканчиваются.
Это хорошая идея, чтобы попытаться избежать дублирования функций. Иногда это может быть сложно, но когда необходимо изменить логику, гораздо проще изменить один метод, который используется повторно, чем изменить целый набор методов со схожей функциональностью.
Обычно геттеры, сеттеры и конструкторы должны быть публичными. Все остальное, что вы хотите попытаться сохранить в тайне, если нет случая, когда другой класс должен выполнить его. Сохранение методов по умолчанию для private поможет поддерживать Encapsulation . То же самое касается полей, по умолчанию привыкайте к приватным
источник
Я не буду отвечать на ваш вопрос, но думаю, что проблема возникает из-за используемых вами терминов. Например
Я думаю, что вам нужен XML, поэтому я создам объект XML, который может быть создан из текстового типа (я не знаю C # ... в Java это называется String). Например
Вы можете проверить это, и это ясно. Затем, если вам нужна фабрика, вы можете добавить фабричный метод (и он может быть статическим, как во втором примере). Например
источник
Вы можете следовать некоторым простым правилам. Это помогает, если вы понимаете причины для правил.
статический метод против экземпляра
Для методов вам не нужно принимать это решение осознанно. Если кажется, что ваш метод не использует никаких элементов поля (ваш любимый анализатор должен вам об этом сказать), вам нужно добавить ключевое слово static.
метод без параметров или возвращаемого значения против метода с параметрами и возвращаемым значением
Второй вариант лучше из-за объема. Вы всегда должны держать свой прицел крепким. Достигать того, что вам нужно, плохо, вы должны иметь вход, работать с ним локально и возвращать результат. Ваш первый вариант плох по той же причине, что глобальные переменные, как правило, плохие: они значимы только для части вашего кода, но они видны (и, следовательно, шум) повсюду в другом месте и могут быть изменены из любого места. Это затрудняет получение полной картины вашей логики.
перекрывающиеся и отличные функциональные возможности метода
Я не думаю, что это проблема. Методы, вызывающие другие методы, хороши, если это разбивает задачи на более мелкие отчетливо функциональные блоки.
приват против публичного метода
Сделайте все частным, если вам не нужно, чтобы это было публично. Пользователь вашего класса может обходиться без шума, он хочет видеть только то, что для него важно.
источник