Создание Catch-All AppToolbox класса - это плохая практика? - PullRequest
6 голосов
/ 31 октября 2009

Никогда не знаете, где разместить такие функции, как:

String PrettyPhone( String phoneNumber ) // return formatted (999) 999-9999
String EscapeInput( String inputString ) // gets rid of SQL-escapes like ' 

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

Ответы [ 8 ]

6 голосов
/ 31 октября 2009

Отличный вопрос. Я всегда нахожу, что любой достаточно сложный проект требует «служебных» классов. Я думаю, это просто потому, что природа объектно-ориентированного программирования вынуждает нас помещать вещи в аккуратно структурированную иерархическую таксономию, когда это не всегда возможно или нецелесообразно (например, попробуйте создать объектную модель для млекопитающих, а затем сожмите утконоса в ). Это проблема, которая мотивирует работу в аспектно-ориентированном программировании (см. сквозная задача ). Часто то, что входит в служебный класс, - это вещи, которые являются сквозными.

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

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

2 голосов
/ 31 октября 2009

Я думаю, что первое, что приходит на ум - статический вспомогательный класс. Это настолько распространено, что некоторые даже называют это частью объектно-ориентированного дизайна. Однако самая большая проблема с вспомогательными классами состоит в том, что они имеют тенденцию становиться большим дампом. Я думаю, я видел, как это произошло в нескольких крупных проектах, в которых я принимал участие. Вы работаете над классом и не знаете, где разместить эту и ту функцию, поэтому поместили ее в свой вспомогательный класс. В этот момент ваши помощники не очень хорошо понимают, что они делают. Само имя «helper» или «util» в имени класса ничего не значит. Я думаю, что почти все гуру OO выступают против помощников, так как вы можете очень легко заменить их более описательными классами, если вы достаточно обдумаете это. Я склонен согласиться с этим подходом, так как считаю, что помощники нарушают принцип единой ответственности. Честно, возьми это с крошкой соли. Я немного самоуверен на ООП:)

2 голосов
/ 31 октября 2009

В этих примерах я был бы более склонен расширять строку:

class PhoneNumber extends String
{
     public override string ToString()
     {
         // return (999) 999-9999
     }
}

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

EDIT:

Как указано ниже, вы не можете переопределить строку в C #. Суть, которую я пытался сделать, заключается в том, что эта операция выполняется для номера телефона, поэтому функция принадлежит ей:

interface PhoneNumber
{
    string Formatted();
}

Если у вас разные форматы, вы можете поменять местами реализации PhoneNumber, не засоряя ваш код операторами if, например,

Вместо:

if(country == Countries.UK) output = Toolbox.PhoneNumberUK(phoneNumber);
else ph = Toolbox.PhoneNumberUS(phoneNumber);

Вы можете просто использовать:

output = phoneNumber.Formatted();
1 голос
/ 31 октября 2009

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

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

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

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

Это работало нормально месяцами. Но наступил момент, когда это только начинало выглядеть уродливо. И поэтому я решил перестроить его в свой класс. И когда я просмотрел свой код, просматривая все места, где вызывался этот метод, стало совершенно ясно, что все настраиваемые методы действительно должны быть членами абстрактного класса, а сборки клиентов должны содержать один производный класс. который реализует все абстрактные методы, а затем программе просто нужно было получить имя сборки и пространство имен из ее конфигурации и создать экземпляр класса пользовательских объектов при запуске. Мне было действительно легко найти все методы, которые нужно было настроить, так как все, что мне нужно было сделать, - это найти все места, где вызывался мой метод load-a-custom-feature. Мне потребовалась лучшая часть дня, чтобы просмотреть всю кодовую базу и рационализировать этот дизайн, а конечный результат действительно гибкий и надежный и решает правильную проблему.

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

1 голос
/ 31 октября 2009

Мой опыт показывает, что вспомогательные функции редко бывают изолированными. Если вам нужен метод для форматирования телефонных номеров, то вам также понадобится метод для проверки телефонных номеров и анализа телефонных номеров. Следуя принципу YAGNI, вы, конечно, не захотите писать такие вещи, пока они на самом деле не нужны, но я думаю, что будет полезно просто пойти дальше и разделить такую ​​функциональность на отдельные классы. Рост этих классов из отдельных методов в второстепенные подсистемы будет происходить естественным образом с течением времени. Я обнаружил, что это самый простой способ сохранить код организованным, понятным и обслуживаемым в течение длительного времени.

1 голос
/ 31 октября 2009

В этом нет ничего плохого. Одно дело - попытаться разбить его на логические части. Делая это, вы можете сохранить ваш intellisense в чистоте.

MyCore.Extensions.Formatting.People
MyCore.Extensions.Formatting.Xml
MyCore.Extensions.Formatting.Html
0 голосов
/ 31 октября 2009

Я отправил комментарий, но подумал, что уточню немного.

Что я делаю, так это создаю общую библиотеку с пространствами имен: [Organization]. [Product] .Common в качестве корневого и вспомогательного пространства имен Helpers.

Несколько человек здесь упоминают такие вещи, как создание класса и распространение чего-то, чего они не знают, куда еще добавить. Неправильно. Я бы сказал, что даже если вам нужен один вспомогательный метод, он связан с чем-то, поэтому создайте статический вспомогательный класс с соответствующим именем (IoHelper, StringHelper и т. Д.) И поместите его в пространство имен Helpers. Таким образом, вы получаете некоторую структуру и разделяете интересы.

В корневом пространстве имен вы можете использовать экземпляры служебных классов, которым требуется состояние (они существуют!). И само собой разумеется, также используйте подходящее имя класса, но не используйте суффикс с помощником.

0 голосов
/ 31 октября 2009

Я думаю, что причина его неодобрения в том, что «набор инструментов» может расти, и вы будете загружать кучу ресурсов каждый раз, когда захотите вызвать одну функцию.

Также более элегантно иметь методы, которые применяются к объектам в реальном классе - просто имеет больше смысла.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...