Является ли это «запахом шаблона» для размещения в вашей модели геттеров типа «FullName» или «FormattedPhoneNumber»?

13

Я работаю над приложением ASP.NET MVC, и у меня появилась привычка вставлять то, что кажется полезным и удобным средством получения, в мои классы моделей / сущностей.

Например:

public class Member
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }

    public string FullName
    {
        get { return FirstName + " " + LastName; }
    }

    public string FormattedPhoneNumber
    {
        get { return "(" + PhoneNumber.Substring(0, 3) + ") " + PhoneNumber.Substring(3, 3) + "-" + PhoneNumber.Substring(6); }
    }
}

Мне интересно, что люди думают о FullNameи FormattedPhoneNumberдобытчиках.

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

Фактически, я первоначально применял эти форматы данных на своем сервисном уровне, где я делаю свое отображение, но становилось бременем постоянно писать программы форматирования, а затем применять их во многих разных местах. Например, я использую «Полное имя» в большинстве представлений, и мне нужно напечатать что-то вродеmodel.FullName = MappingUtilities.GetFullName(entity.FirstName, entity.LastName); повсюду казалось менее элегантной, чем просто печатать model.FullName = entity.FullName(или, если вы используете что-то вроде AutoMapper, возможно, вообще ничего не печатать).

Итак, где вы проводите черту, когда дело доходит до форматирования данных. Это нормально для форматирования данных в вашей модели или это «запах шаблона»?

Примечание: у меня точно нет html в моей модели. Я использую помощники HTML для этого. Я строго говорю о форматировании или объединении данных (и особенно данных, которые часто используются).

devuxer
источник
1
Делать это может быть удобно. Но надейтесь и молитесь, чтобы вам никогда не пришлось интернационализировать этот код.
btilly
@ Хорошая мысль, но я на 99,99% уверен, что не буду.
devuxer
Определенно для FullName и PhoneNumber, определенно. Является ли вопрос именно о них, потому что они имеют несовместимые форматы в разных культурах, или же @DanM просто выбрал примеры, которые плохо интернационализируются для более общего вопроса?
Грег Джексон
@ Грег Джексон, определенно лестница. Как указал ammoQ, PhoneNumberвероятно, принадлежит к своему классу (который я сейчас реализовал). Но это FullNameбыл действительно тот, который побудил меня написать вопрос. Но мне интересно узнать, имеет ли смысл в общем случае форматировать / комбинировать данные и т. Д. В модели для вещей, которые будут применяться в масштабах всего приложения. Судя по ответам ниже, кажется, что это не анти-паттерн, но решение должно приниматься осторожно.
devuxer
Имейте в виду, что это конкретное форматирование для полного имени также может плохо интернационализироваться. Например, в Восточной Азии порядок имен обратный тому, что есть в западном мире. Вы действительно должны явно обращаться с этим? Возможно, нет, но просто имейте в виду, что при форматировании данных вы можете столкнуться с множеством хитрых вещей.
Грег Джексон

Ответы:

9

В вашем примере мне нравится метод получения FullName(по всем причинам, которые вы указали), но мне не нравится метод получения FormattedPhoneNumber. Причина в том, что это, вероятно, не так просто (если у вас есть международные телефонные номера и т. Д.), И если вы разместите логику для форматирования телефонных номеров в методе Member, скорее всего, вам потребуется рефакторинг (или копирование-вставка caugh ), как только вы нужен форматированный номер телефона Institutionи Vendorт. д.

РЕДАКТИРОВАТЬ: ИМО было бы лучше иметь PhoneNumberкласс с Formattedгеттером.

user281377
источник
+1 и спасибо. Но что, если я на самом деле введу код форматирования моего номера телефона в метод расширения? Тогда любая модель могла бы использовать его, и не было бы рефакторинга или копирования / вставки. Это решение было бы намного менее повторяющимся, чем применение форматера к каждому номеру телефона, который появляется в каждом представлении.
devuxer
3
Использование метода расширения для этого было бы быстрым путем к антипаттерну «функциональной декомпозиции». Используя метод расширения (для Stringкласса, я полагаю), вы «учите» Stringкласс, как форматировать телефонные номера. Это действительно обязанность Stringкласса знать о телефонных номерах? Я так не думаю. Используемые таким образом методы расширения являются синтаксическим сахаром, позволяющим чему-то явно не объектно-ориентированному выглядеть так, как это было раньше.
user281377
1
Хорошо, забудь, что я сказал метод расширения. Притворись, я сказал, служебный метод или класс форматера. Я просто говорю, что рефакторинг / копирование не должно быть необходимым, независимо от того, есть ли у меня модель получения или я выполняю форматирование где-то еще.
devuxer
1
И мне действительно нравится идея PhoneNumberкласса. В любом случае я планировал сделать это, потому что у меня есть PhoneTypeсобственность.
devuxer
Мне не нравится идея иметь PhoneNumberв качестве экземпляра класса, потому что данные изначально string. Скорее, это должен быть статический класс с такими методами, как public static string Format(string phoneNumber, PhoneNumberStyle style).
г-н Андерсон
5

Что нужно учитывать при написании кода: правильно ли это? Это читабельно? Это эффективно? Это ремонтопригодно? Я бы сказал, как упомянул @btilly, что это не поддерживается из-за специфического для культуры форматирования, но вопрос, кажется, более общий, чем этот.

Использование таких методов доступа делает ваш код более читабельным и, в зависимости от того, как вы его используете, может сделать другие части вашего кода намного чище. На мой взгляд, это совсем не пахнет. Запах бы начался, если бы у вас были средства форматирования для любой строки, которую вы захотите напечатать ( public string FirstLastName; public string FullName; public string FullNameWithMiddleInitial; public string PhoneNumberWithAreaCode; public string PhoneNumberWithoutAreaCode; public string PhoneNumberWithCountryCode;и т. Д.)

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

Грег Джексон
источник
Спасибо, Грег. +1. Я согласен с вами по поводу сдачи каждой комбинации. Я просто пытаюсь найти самый простой способ стандартизировать способ просмотра данных.
devuxer
3

Нарушает принцип единоличной ответственности. Почему бы не сделать класс номера телефона и т.д ...?

Эдвард Стрендж
источник
Хорошо, я на самом деле согласен с этим (см. Обсуждение под ответом ammoQ), но я должен также сделать FullNameкласс?
devuxer
Да, вы должны, но вы должны исправить написание от "FullName" до "FoolName". Или, может быть, «PersonalName», так как это имя человека, а не полное.
Кевин Клайн
1
В самом деле? Если ожидается, что данный класс будет расти, что именно с ним не так? Даже если он действительно вырастет, насколько трудно будет тогда пересмотреть его?
Работа
@DanM: Тогда это то, что вы просите, то есть попросите их ввести свое полное официальное имя. Если вы сортируете по имени, вы действительно сортируете по первой букве (затем по второй и т. Д.) Первого имени (затем по следующему и т. Д.), Поэтому имена будут сортироваться одинаково независимо от них.
Мэтт Эллен
@DanM, давайте продолжим эту дискуссию в чате
Мэтт Эллен,
1

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

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

Если бы это случилось, я бы соблазнился Memberвернуть класс к:

public class Member
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }  
}

А потом делайте разные адаптеры для каждой цели. Например, предположим, что информация требовалась в формате CSV:

public static class CSVMemberAdapter
{
    public static string ToCSV(this Member mbr)
    {
         return mbr.Id + "," + mbr.LastName + "," + mbr.FirstName, "," mbr.PhoneNumber;
    }
}

Всегда предполагая, что вы санировали данные, чтобы в строках не было запятых и т. Д.

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

Питер К.
источник
Прошло много времени с тех пор, как я написал C #, но, безусловно, есть классы сериализации, которые могут справиться с этим, вместо утомительного написания таких методов, как toCSV, снова и снова, снова и снова, снова и снова, снова и снова, снова и снова, снова и снова, снова и снова и снова и снова и снова и снова и снова ...
Кевин Клайн
@kevin cline: Это зависит от того, что вам нужно в каждой раковине. Если все, что им нужно, это сериализованный XML, хорошо. Многие системы этого не делают. Если это нужно делать overстолько раз, то с дизайном что-то не так.
Питер К.
как правило, было бы много классов для сериализации. Должна быть возможность написать один CSV или другой сериализатор, который мог бы обрабатывать большинство классов с помощью отражения, а не кодировать их вручную, как в вашем примере.
Кевин Клайн
@kevin cline: яростно согласен! Я просто писал что-то очень специфичное для заданного вопроса. Конкретные, простые примеры, как правило, объясняют вещи лучше.
Питер К.