Czy to „zapachowy wzór”, który umieszcza w twoim modelu takie jak „FullName” lub „FormattedPhoneNumber”?

13

Pracuję nad aplikacją ASP.NET MVC i przyzwyczaiłem się do umieszczania w moich modelach / obiektach elementów, które wydają się przydatne i wygodne.

Na przykład:

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

Zastanawiam się, czy ludzie myślą o FullNamei FormattedPhoneNumberłapaczach.

Ułatwiają tworzenie znormalizowanych formatów danych w całej aplikacji i wydają się oszczędzać wiele powtarzającego się kodu, ale zdecydowanie można argumentować, że format danych jest czymś, co powinno być obsługiwane przy mapowaniu z modelu na model widoku.

W rzeczywistości pierwotnie stosowałem te formaty danych w warstwie usług, w której wykonuję mapowanie, ale ciężko było ciągle pisać formaty, a następnie stosować je w wielu różnych miejscach. Na przykład używam „Pełna nazwa” w większości widoków, a konieczność wpisywania czegoś podobnego w model.FullName = MappingUtilities.GetFullName(entity.FirstName, entity.LastName);całym miejscu wydawała się o wiele mniej elegancka niż zwykłe pisanie model.FullName = entity.FullName(lub, jeśli używasz czegoś takiego jak AutoMapper, potencjalnie w ogóle nic nie wpisując).

Gdzie narysujesz linię, jeśli chodzi o formatowanie danych. Czy formatowanie danych w twoim modelu jest „w porządku” czy jest to „zapach wzorowy”?

Uwaga: zdecydowanie nie mam html w moim modelu. Używam do tego pomocy HTML. Mówię ściśle o formatowaniu lub łączeniu danych (a zwłaszcza danych często używanych).

devuxer
źródło
1
Może to być wygodne. Miejcie jednak nadzieję i módlcie się, abyście nigdy nie musieli internacjonalizować tego kodu.
btilly,
@btilly, dobra uwaga, ale jestem około 99,99% pewien, że nie zrobię tego.
devuxer
Zdecydowanie specyficzne dla FullName i PhoneNumber. Czy pytanie dotyczy konkretnie tych, ponieważ mają niespójne formaty w różnych kulturach, czy też @DanM po prostu wybrał przykłady, które nie są dobrze internacjonalizowane w przypadku bardziej ogólnego pytania?
Greg Jackson
@Greg Jackson, zdecydowanie drabina. Jak wskazał ammoQ, PhoneNumberprawdopodobnie należy do własnej klasy (którą teraz zaimplementowałem). Ale FullNametak naprawdę to on zmotywował mnie do napisania pytania. Ale chcę dowiedzieć się, czy generalnie sensowne jest umieszczenie formatowania / czesania danych itp. W modelu dla rzeczy, które będą obowiązywać w całej aplikacji. Z poniższych odpowiedzi wynika, że ​​nie jest to anty-wzór, ale decyzję należy podjąć ostrożnie.
devuxer
Należy pamiętać, że to specyficzne formatowanie pełnej nazwy może również nie być dobrze umiędzynarodowione. Na przykład w Azji Wschodniej kolejność nazw jest odwrotna do tej w świecie zachodnim. Czy naprawdę musisz to wyraźnie obsłużyć? Może nie, ale pamiętaj, że podczas formatowania danych można napotkać wiele trudnych rzeczy.
Greg Jackson

Odpowiedzi:

9

W twoim przykładzie podoba mi się FullNamegetter (z wszystkich podanych przez ciebie powodów), ale nie podoba mi się getter FormattedPhoneNumber. Powód jest taki: prawdopodobnie nie jest to takie łatwe (kiedy masz międzynarodowe numery telefonów itp.), A jeśli umieścisz logikę do formowania numerów telefonów w sposób Member, istnieje szansa, że ​​będziesz musiał refaktoryzować (lub skopiować i wkleić kaukas ), gdy tylko potrzebuję sformatowany numer telefonu Institution, Vendoritp też.

EDYCJA: IMO lepiej byłoby mieć PhoneNumberklasę z Formattedgetterem.

użytkownik 281377
źródło
+1 i dzięki. Ale co, jeśli faktycznie zastosuję kod formatowania numeru telefonu w metodzie rozszerzenia? Wtedy każdy model mógłby z niego skorzystać i nie byłoby refaktoryzacji ani kopiowania / wklejania. To rozwiązanie byłoby znacznie mniej powtarzalne niż stosowanie formatyzatora do każdego numeru telefonu wyświetlanego w każdym widoku.
devuxer
3
Użycie do tego metody rozszerzenia byłoby szybkim sposobem na uzyskanie antyatatternu „funkcjonalnego rozkładu”. Używając metody rozszerzenia ( Stringzakładam, że dla klasy) „uczysz” Stringklasę formatowania numerów telefonów. Czy naprawdę obowiązkiem Stringklasy jest wiedzieć o numerach telefonów? Nie wydaje mi się Stosowane w ten sposób metody rozszerzania są cukrem syntaktycznym, aby coś, co wyraźnie nie jest zorientowane obiektowo, wyglądało tak, jak było.
user281377
1
Okej, zapomnij, że powiedziałem metodę rozszerzenia. Udawaj, że mówiłem o metodzie użytkowej lub klasie formatera. Mówię po prostu, że refaktoryzacja / wklejanie kopii nie powinno być konieczne, niezależnie od tego, czy mam model do pobierania, czy formatuję gdzie indziej.
devuxer
1
I podoba mi się pomysł PhoneNumberklasy. W każdym razie planowałem to zrobić, ponieważ mam również PhoneTypenieruchomość.
devuxer
Nie podoba mi się pomysł posiadania PhoneNumberjako klasy instancji, ponieważ dane są natywnie string. Raczej powinna to być klasa statyczna z takimi metodami public static string Format(string phoneNumber, PhoneNumberStyle style).
Pan Anderson
5

Rzeczy, które należy wziąć pod uwagę przy pisaniu kodu: czy to prawda? Czy to jest czytelne? Czy to jest wydajne? Czy można to utrzymać? Argumentowałbym, jak wspomniał @btilly, że nie można go utrzymać z powodu formatowania specyficznego dla kultury, ale pytanie wydaje się bardziej ogólne.

Korzystanie z takich akcesoriów powoduje, że kod jest bardziej czytelny i, w zależności od tego, jak go używasz, może uczynić inne części kodu znacznie czystszymi. Moim zdaniem wcale nie pachnie. Zacznie pachnieć, jeśli będziesz mieć akcesoria do formatowania dowolnego rodzaju ciągu, który chcesz wydrukować ( public string FirstLastName; public string FullName; public string FullNameWithMiddleInitial; public string PhoneNumberWithAreaCode; public string PhoneNumberWithoutAreaCode; public string PhoneNumberWithCountryCode;itp.)

Lub, inaczej mówiąc, użycie wzorca nie powoduje automatycznie, że kod ma „zapach wzorca”. Musisz go nadużyć, jeśli chcesz zdobyć ten atrybut.

Greg Jackson
źródło
Dzięki, Greg. +1. Zgadzam się z tobą w sprawie umieszczenia każdej kombinacji. Naprawdę staram się znaleźć najczystszy sposób na ustandaryzowanie sposobu wyświetlania danych.
devuxer
3

Łamie zasadę pojedynczej odpowiedzialności. Dlaczego nie stworzyć klasy numeru telefonu itp.?

Edward Strange
źródło
Okej, właściwie się z tym zgadzam (patrz dyskusja pod odpowiedzią ammoQ), ale czy powinienem także zrobić FullNamezajęcia?
devuxer
Tak, powinieneś, ale należy poprawić pisownię z „FullName” na „FoolName”. A może „PersonalName”, ponieważ jest to imię osoby, a nie pełne.
kevin cline
1
Naprawdę? O ile nie oczekuje się, że dana klasa wzrośnie, co dokładnie jest z nią nie tak? Nawet jeśli będzie rosnąć, to jak trudno byłoby to zmienić na nowo?
Job
@DanM: Więc o to prosisz, tzn. Poproś ich o wpisanie pełnej nazwy prawnej. Jeśli sortujesz według imienia, tak naprawdę sortujesz według pierwszej litery (potem drugiej itd.) Pierwszego imienia (potem następnej, potem itd.), Więc nazwy będą sortowane tak samo niezależnie.
Matt Ellen
1

W twoim przykładzie nie uważam tego za zbyt wielką trudność w używaniu określonych formatów. Jest to jedna lub dwie i wszystkie części aplikacji używają tego samego formatu.

Decyzja zaczyna się rozpadać, gdy te same dane trafiają do kilku różnych miejsc wymagających różnych formatów .

Gdyby tak się stało, miałbym pokusę, aby przyciągnąć Memberklasę z powrotem do:

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

Następnie wykonaj różne adaptery dla każdego celu. Załóżmy na przykład, że informacje były wymagane w formacie CSV:

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

Zawsze zakładając, że dane zostały zdezynfekowane, aby przecinki nie były w ciągach.

Adapter nie musi być metodą rozszerzenia, ale w tym przypadku, który wydaje się odpowiedni, wydaje się pasować.

Peter K.
źródło
Minęło trochę czasu, odkąd napisałem C #, ale z pewnością istnieją klasy serializacji, które mogą sobie z tym poradzić, zamiast żmudnego pisania metod takich jak CSV w kółko w kółko w kółko w kółko w kółko w kółko w kółko w kółko i w kółko, w kółko, w kółko ...
Kevin Cline,
@kevin cline: To zależy od tego, czego potrzebujesz przy każdym zlewie. Jeśli wszystko, czego potrzebują, to serializowany XML, w porządku. Wiele systemów tego nie robi. Jeśli trzeba to robić overtyle razy, to coś jest nie tak z projektem.
Peter K.
zazwyczaj istnieje wiele klas do serializacji. Powinno być możliwe napisanie pojedynczego pliku CSV lub innego serializatora, który mógłby obsługiwać większość klas poprzez refleksję, zamiast kodować je ręcznie, tak jak w twoim przykładzie.
kevin cline
@kevin cline: Gwałtownie się zgadzam! Właśnie pisałem coś bardzo konkretnego do zadanego pytania. Konkretne, proste przykłady lepiej wyjaśniają sprawy.
Peter K.