web-dev-qa-db-ja.com

オブジェクトとプリミティブを渡す

変数を渡すときのベストプラクティスは何ですか?オブジェクト(モデル)を渡す方が良いですか、それとも、呼び出されたメソッドが必要とするプリミティブを渡す方が良いですか?両方のアプローチの長所と短所は何ですか?たとえば、値を渡すことの1つの欠点(例2)で、ユーザーの名が突然必要になったときに、インターフェイスのメソッドシグネチャを変更する必要があります。オブジェクトを渡すことの1つの欠点は、そのインターフェースに密接に結合することです。

例1:

class CustomerService{
    public function emailCustomer(UserModel $user, string $message)
    {
        //Get email address from model and send email
    }
}

例2:

class CustomerService{
    public function emailCustomer(string $userEmail, string $message)
    {
        //send email
    }
}
4
Tali Luvhengo

最近、Mark Seemanの古いブログ投稿 境界ではアプリケーションはオブジェクト指向ではありません に遭遇しました。これは、どのアプローチが最適かを判断するのに役立つと思います。そしていつ。

一言で言えば、それはあなたが取り組んでいる抽象化のレベルに依存します。それぞれの境界からどれだけ近い(または遠い)か。

ソフトウェアアーキテクチャは、境界と呼ばれる線を描く技術です。これらの境界はソフトウェア要素を互いに分離し、一方の境界を他方の境界について知ることを制限します。

Clean Architecture:A Craftsman's Guide to Software Structure and Design、First Edition by Robert C. Martin

メールサービスのドメインを実装するとします。 R.C.Mによると、私たちはアーキテクチャの最も内側の層に取り組んでいます。コア。このレベルでの目標は、ドメインをモデル化して、可能な限り ユビキタス言語 を呼び起こすことです。

ドメイン層-データモデル

public interface Sender {

  void send(MimeMessage message);

}

MimeMessageはドメインの最初の市民クラスです。それは不変の面倒を見て、メールのメッセージが何であるかを最もよく説明します。

public MimeMessage {
   private InternetAddress from;
   private InternetAddress[] recipients;
   private InternetAddress[] cc;
   private InternetAddress[] bcc;
   private BodyPart bodyPart;
   private BodyMultiPart multipart;
   private String subject;

   public MimeMessage(
         InternetAddress from, 
         InternetAddress[] recipients, BodyPart bodyPart){..}

   public void addMultipart(BodyMultipart attachment){..}
}

ここで、ドメインの抽象化の上位レベルを越えて、実装の詳細が遠くに見え始め、外層の境界が近くに見え始める境界まで移動します。ここでMark Seemanのブログ投稿が意味を持ち始めます。

境界で、物事が密結合する危険にさらされている場合、物事を少なくすることは、OOは生活を楽にするので便利かもしれません。しかし、表現力。

ドメイン層-サービス

public interface MailService {
  void send(String from, String[] to, String subject, String message);
  void send(String from, String[] to, String subject, String message, File[] attachments);

}

Mark Seemanは、representデータへの方法と同様に、データ構造の概念を紹介しますレイヤーからレイヤーへの移行の瞬間のモデル。コンポーネントをネットワークを介して互いに通信する異なるプロセスであるかのように通信するためのデータ構造。つまり、シリアライゼーション。

それについて考えてください。ドメインの境界のエッジにあるMimeMessageは、たとえばformatted stringとして、文字列のarrayとして、Mapまたはとしてシリアル化できます。オブジェクトのSet、または5つの異なるStringsとして primitive obsession に分類されない理由は、その使用法が非常に特定の範囲に限定されているためです。層間通信。 MimeMessageは、その表現力が重要な場合(ドメインの境界内)に使用されます。

まとめ

両方を使用できるのに、なぜ選択を1つまたは別のソリューションに制限するのですか?適切な場所を見つけて一貫性を保つことが求められるだけです。問題は、両方のソリューションが相互に排他的ではないということです。

代替案は、全体的な複雑さを増すようなものです。場合によっては、増加が正当化される場合もあれば、議論の余地がある場合もあるので、実際的です。


1:@DavidArnoと@Robertはすでに行ったと思うので、賛否両論は列挙しません。

5
Laiv

「オブジェクト」の意味によって異なります。

データ構造またはプロパティのバッグを意味する場合、事実上まったく違いはありません。もちろん、いくつかの構文上の違いがあります。少量のプリミティブを渡す方が多少便利かもしれませんが、「オブジェクト」を渡す方がタイプ的に安全です。

利便性と保守性の本当の違いは、オブジェクトをbehaviorを持つ本当の概念として使用し始めるときです。プリミティブまたはオブジェクトを使用することはyourの選択ではなく、動作のモデリングの結果であることに気付くでしょう。ビジネス関連の振る舞いがないものは原始的なままであり、そうするものは自然にオブジェクトを形成します。

注:オブジェクト名に「Service」および「Model」接尾辞を使用することは、動作をモデル化するためにまだオブジェクトを使用していないことを示唆しています。

1

変数の受け渡しに関するベストプラクティスは何ですか。オブジェクト(モデル)を渡す方が良いですか、呼び出されたメソッドが必要とするプリミティブを渡す方が良いですか

プリミティブを渡すことは 広くコードの匂い、別名プリミティブへのこだわり と見なされます。 「原始的な強迫観念」の単純なウェブ検索は、なぜそれが悪い考えであるかについて多くの記事を提供します。

一般に、オブジェクトを使用する利点は次のとおりです。

  1. 明快さが向上しました。文字列は何でもかまいません。 UserModelは、古いデータではなく、ユーザーモデルを扱っていることを示しています。
  2. データ整合性の向上。この場合も、文字列には何でも含めることができます。たとえば、メールアドレスではなくユーザー名を誤って渡してしまう可能性があります。そのため、メソッド内でその文字列を検証することを検討する必要があります。 UserModelを渡してください。そうすれば、これらの問題は減少します。
  3. 潜在的に優れたコード構成と重複のリスクの低減。たとえばメールアドレスに対して実行したいアクションがある場合、それらのアクションはすべてUserModelクラス内に存在する可能性があります。これは非常に「リッチドメインモデル」ですOO「メリット」ですが、あなたには関係がないかもしれません。

見つけるのがさらに難しいのは、コードの匂いではない場合の詳細、および/またはプリミティブの代わりに単純なオブジェクトを使用する場合の欠点です。簡単に言えば、原始的な強迫観念による強迫観念は、コードの過剰設計につながる可能性があります。

  1. 文字列を使用するだけでなく、UserModelクラスを記述、テスト、および保守する必要があります。コードを複雑にしました。
  2. UserModelは、特にAPI境界で広く使用されていますか?もしそうなら、それを使用することは理にかなっています。しかし、それ以外の場合、その追加の型が、追加された複雑さを上回る十分な利点をもたらすかどうかは疑問です。

要約すると、APIを形成してオブジェクトを使用するメソッドのプリミティブは避けてください。ただし、内部実装詳細コードの場合、物事を単純に保つためには、プリミティブの方が適している場合があります。

1
David Arno

インターフェイスタイプとして「文字列」を使用できます。 「EmailAddress」というクラスを作成すると、インターフェイスタイプの候補になる可能性が非常に高くなります。 「ModelUser」には、StringまたはEmailAddressメンバーが含まれる場合があります。またはそれがいくつかあるかもしれません。

最高のインターフェースは「EmailAddress」でしょう。メールアドレスを含む文字列からメールアドレスを作成でき、ModelUserからメールアドレスを取得するメソッドが必要です。 2番目に最適なのはStringです。特に、EmailAddressクラスがない場合はそうです。 "ModelUser"は、ユーザーが複数の電子メールアドレスを持つことができる場合に問題があります。これは、使用する電子メールアドレスを選択する方法が必要になるためです。

0
gnasher729

一般的に、より意味のあるものを使用します。

オブジェクトの長所の使用:

  • パラメータを集約します。 drawPoint(int x, int y, int z)を使用する代わりに、drawPoint(Point3D point)を使用する方が便利です
  • 読みやすい(ポイントの同じ例)。

オブジェクトの短所の使用:

  • カップリングを増やします。 UserModelを変更すると、emailCustomerも変更する必要がある場合があります。
  • テストが難しい。ここで、単純な文字列を送信する代わりに、UserModelをモックする必要があります。
  • 誤用しやすい デメテルの法則 を参照

何かを証明するために本や文書を参照することはありませんが、それは長所と短所を考慮して、使用方法に完全に依存していると言えます。したがって、私はいつも使用しようとするより良い方法を提供することができます:

  1. Publicメソッドのパラメーターとしてインターフェイスを使用します。

    あなたの署名は多くの場所で使用でき、それらを変更することは本当に難しいです。これを無視するには、インターフェイスをできるだけパラメーターとして使用できます。インターフェースの操作が難しいと思われる場合は、代わりに具象クラスを使用できます。

  2. 内部プライベートビジネスメソッドにプリミティブ型をできるだけ使用するようにしてください。

    インターフェイスまたは具象クラスを使用するには、初期化とnull参照制御が必要です。プリミティブ型をパラメーターとして使用することにより、ビジネスメソッドを簡略化できます。さらに、参照型の管理は簡単ではありません。参照タイプの項目の値を変更する代わりに、計算された値を返し、呼び出し元から変更できるようにします。もちろん、パラメータとしてインターフェースまたは具象クラスを必要とするプライベートクラスを作成する必要がありますが、メインのアプローチは変更されません。プライベートメソッドをできるだけシンプルにすることをお勧めします。

0
Engineert