モデルに「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); }
    }
}

FullNameFormattedPhoneNumberゲッターについて人々が考えるのだろうか。

アプリ全体で標準化されたデータ形式を非常に簡単に作成でき、繰り返しコードを大量に保存するように見えますが、データ形式はモデルからビューモデルへのマッピングで処理されるべきものであると断言できます。

実際、私は元々、これらのデータ形式をマッピングを行うサービスレイヤーに適用していましたが、フォーマッタを絶えず書き、それをさまざまな場所に適用しなければならないのは負担になりました。例えば、私はほとんどのビューで「フルネーム」を使用model.FullName = MappingUtilities.GetFullName(entity.FirstName, entity.LastName);し、model.FullName = entity.FullNameあちこちに何かを入力しなければならないことは、単に入力するよりもはるかにエレガントではないように見えました(または、AutoMapperのようなものを使用すると、何も入力しない可能性があります)。

それで、データのフォーマットに関してはどこで線を引きますか。モデルでデータのフォーマットを行うのは「大丈夫」ですか、それとも「パターンの匂い」ですか?

注:私のモデルにはHTMLが含まれていません。そのためにhtmlヘルパーを使用します。データ(特に頻繁に使用されるデータ)のフォーマットまたは結合について厳密に説明しています。


1
これを行うと便利です。しかし、そのコードを国際化する必要がないことを願って祈ってください。
-btilly

@btilly、良い点ですが、私はそうしないと確信している約99.99%です。
-devuxer

FullNameとPhoneNumberに固有の、間違いなく。それらは異なる文化で一貫性のない形式を持っているため、または@DanMはより一般的な質問のために国際化がうまくいかない例を単に選んだため、それらについて特に質問ですか?
グレッグジャクソン

@グレッグジャクソン、間違いなくはしご。ammoQが指摘したように、PhoneNumberおそらく独自のクラスに属します(現在実装しています)。しかし、FullName本当に私が質問を書くように動機付けたものでした。しかし、一般的に、アプリ全体に適用されるもののモデルにデータのフォーマット/コーミングなどを入れることが理にかなっているかどうかを調べることに興味があります。以下の回答から、これはアンチパターンではないと思われますが、慎重に決定する必要があります。
-devuxer

このフルネームの特定の書式設定も国際化に適さない可能性があることに注意してください。たとえば、東アジアでは、名前の順序は西側世界とは逆です。本当にこれを明示的に処理する必要がありますか?そうではないかもしれませんが、データをフォーマットするときに出くわすトリッキーなことがたくさんあることに留意してください。
グレッグジャクソン

回答:


9

あなたの例では、FullName(あなたが与えたすべての理由で)ゲッターは好きですが、FormattedPhoneNumberゲッターは好きではありません。その理由は次のとおりです。それは(あなたが国際電話番号などを持っていたら)、あなたはの方法で電話番号をフォーマットするためのロジックを配置する場合は簡単ということはおそらくないMember、チャンスはあなたがリファクタリング(またはコピー&ペーストにする必要がありますされているcaughます1回)以下のためにフォーマットされた電話番号を必要とするInstitutionVendorあまりになど。

編集:IMO PhoneNumberFormattedゲッターを持つクラスを持つ方が良いでしょう。


+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:それはあなたが求めるものです。すなわち、彼らに彼らの正式な名前をタイプするよう頼んでください。名前で並べ替える場合、実際には名の最初の文字(次に2番目など)で並べ替えるので、名前は関係なく同じように並べ替えられます。
マットエレン


1

あなたの例では、特定の形式を使用するのにあまりにも大したことではないと思います。1つまたは2つで、アプリケーションのすべての部分で同じ形式が使用されます。

その決定が崩れ始めるのは、同じデータが異なるフォーマットを必要とするいくつかの異なる場所に行くときです。

それが起こった場合、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同じくらい何度も行わなければならない場合、デザインに何か問題があります。
ピーターK.

通常、シリアル化するクラスは多数あります。あなたの例のようにそれらを手動でコーディングするのではなく、リフレクションを介してほとんどのクラスを処理できる単一のCSVまたは他のシリアライザを書くことが可能であるべきです。
ケビンクライン

@kevin cline:激しく同意します!質問に対して非常に具体的なものを書いていました。具体的で単純な例は、物事をよりよく説明する傾向があります。
ピーターK.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.