使用するOOデザイン(デザインパターンはありますか?)


11

「バー/クラブ」(あなたが飲む/社交する場所)を表す2つのオブジェクトがあります。

あるシナリオでは、バーの名前、住所、距離、スローガンが必要です

別のシナリオでは、バー名、住所、ウェブサイトのURL、ロゴが必要です

したがって、同じものを表すが異なるフィールドを持つ2つのオブジェクトがあります。

不変オブジェクトを使用するのが好きなので、すべてのフィールドはconstructorから設定されます。

1つのオプションは、2つのコンストラクターを持ち、他のフィールドをヌルにすることです。

class Bar {
     private final String name;
     private final Distance distance;
     private final Url url;

     public Bar(String name, Distance distance){
          this.name = name;
          this.distance = distance;
          this.url = null;
     }

     public Bar(String name, Url url){
          this.name = name;
          this.distance = null;
          this.url = url;
     }

     // getters
}

ゲッターを使用するときにnullチェックを行う必要があるため、私はこれが好きではありません

私の実際の例では、最初のシナリオには3つのフィールドがあり、2番目のシナリオには約10があるので、2つのコンストラクター、nullを宣言しなければならないフィールドの量、そしてオブジェクトが使用されているときに、Barどのユーザーがどこを使用しているか、そしてどのフィールドがヌルで何がそうでないかを知っている。

他にどんなオプションがありますか?

2つのクラスが呼ばれるBarPreviewBar

ある種の継承/インターフェース?

他に素晴らしいものはありますか?


29
うわー、あなたは実際にBar識別子としての合法的な使用を考え出しました!
メイソンウィーラー

1
いくつかのプロパティを共有している場合、1つのオプションは基本クラスを実装することです。
ユスボフ

1
私はそれを考えたことがありませんでした。Bar / Fooドッグパーラー用にあらゆる種類のコードを書くと、本当に混乱する可能性があります。
エリックReppen


4
@gnat人々はどのように推測していますか。あなたのリンクの引用から:You should only ask practical, answerable questions based on actual problems that you face.そしてそれはまさにここで起こっていることです
ブランデル

回答:


9

私の考え:

ドメインに表示される「バー」には、名前、住所、URL、ロゴ、スローガン、および「距離」(リクエスターの場所から推測しています)のいずれかの場所で必要なすべてのものがあります。したがって、ドメインには、後でデータがどこで使用されるかに関係なく、1つのバーのデータの信頼できるソースである「Bar」クラスが1つ必要です。必要に応じてバーのデータを変更して保存できるように、このクラスは可変である必要があります。

ただし、このBarオブジェクトのデータが必要な場所は2つあり、両方ともサブセットのみが必要です(そのデータを変更したくない場合)。通常の答えは「データ転送オブジェクト」またはDTOです。不変のプロパティゲッターを含むPOJO(従来のJavaオブジェクト)。これらのDTOは、メインBarドメインオブジェクトのメソッド「toScenario1DTO()」および「toScenario2DTO()」を呼び出すことで生成できます。結果は水和DTOになります(つまり、1か所で長くて複雑なコンストラクターを使用するだけで済みます)。

メインドメインクラスにデータを送り返す必要がある場合(更新するために、実際の世界の現在の状態を反映するために必要に応じてデータを変更できない場合、データのポイントは何ですか?)、 DTO、または新しい可変DTOを使用し、「updateFromDto()」メソッドを使用してBarクラスに戻します。

編集:例を提供するには:

public class Bar {
     private String name;
     private Address address; 
     private Distance distance;
     private Url url;
     private Image logo;
     private string Slogan;

     public OnlineBarDto ToOnlineDto()
     {
         return new OnlineBarDto(name, address, url, logo);
     }

     public PhysicalBarDto ToPhysicalDto()
     {
         return new PhysicalBarDto(name, address, distance, slogan);
     }

     public void UpdateFromDto(PhysicalBarDto dto)
     {
         //validation logic here, or mixed into assignments

         name = dto.Name;
         address = dto.Address;
         distance = dto.Distance;
         slogan = dto.Slogan;
     }

     public void UpdateFromDto(OnlineBarDto dto)
     {
         //Validate DTO fields before performing assignments

         name = dto.Name;
         address = dto.Address;
         url= dto.Url;
         logo = dto.Logo;
     }

     // getters/setters - As necessary within the model and data access layers;
     // other classes can update the model using DTOs, forcing validation.
}

public class PhysicalBarDto
{
     public final String Name;
     public final Address Address;
     public final Distance Distance;
     public final String Slogan;

     public PhysicalBarDto(string Name, Address address, Distance distance, string slogan) 
     { //set instance fields using parameter fields; you know the drill }
}

public class OnlineBarDto
{
     public final String Name;
     public final Address Address;
     public final Image Logo;
     public final Url Url;

     public OnlineBarDto(string Name, Address address, Url url, Image logo) 
     { //ditto }
}

Address、Distance、およびUrlクラスは、それ自体が不変であるか、DTOで使用される場合、不変の対応するクラスに置き換えられる必要があります。


頭字語DTOは何を表していますか?あなたの言ったことをうまく動作した例に入れてください。fyiデータはサーバーから取得されるため、このクラスのいずれかの形式が「水和」されると、フィールドを変更する必要はなく、UIに表示するためだけに使用されます
Blundell

1
DTOは「データ転送オブジェクト」の略で、実際のドメインレイヤーをUIに公開せずに、「リッチ」ドメインレイヤーからUIなどの上位レイヤーにデータを移動するために使用される非常に単純な構造のデータクラスを指しますDTOを変更する必要がない限り、UIに影響を与えずにドメインに作成されます)。
キース

可変性は、変更や永続性にこれまで影響を及ぼしません。

4
@JarrodRoberson-冗談ですか?クラスが不変(インスタンス化後にインプレースで変更できない)である場合、データレイヤーのデータを変更する唯一の方法は、異なるメンバーを持つ同じレコード(同じPK)を表す新しいインスタンスを構築することです。新しいインスタンスを生成するメソッドを「変更」することで簡単になりますが、変更と永続化には大きな影響があります。
キース

1
@JarrodRoberson コミュニティを聞いてください。あなたは間違っている。。実際には、我々はボードの周りに学校教育いくつかの基本的なOOが必要であることを、この全体の回答ショーで半分のコメント-それは嫌だ...
デヴィッド・コーデン

5

プロパティのサブセットのみに関心があり、それらが混同されないようにしたい場合は、2つのインターフェイスを作成し、それを使用してベースオブジェクトと通信します。


1
あなたはそれを言うが、Barクラスを使用して例を与えることができます
ブランデル

3

ここでは、Builderパターン(またはそれに近いもの)が役に立つかもしれません。

不変オブジェクトを持つことは素晴らしいことですが、JavaのReflectionでは、本当に安全なものは何もありません;-)。


HawaiianPizzaBuilder必要な値がハードコーディングされているため、どのように機能するかがわかります。しかし、値が取得されてコンストラクタに渡される場合、このパターンをどのように使用できますか?にはnullが可能なHawaiianPizzaBuildergetterがすべてSpicyPizzaBuilder含まれます。これを@Jarrodsと組み合わせない限りNull Object Pattern。を使用したコード例Barは、あなたのポイントを
理解

+1私はこのような場合にビルダーを使用し、チャームのように動作します
-gnat

3

ここで重要なポイントは、間の差である「バー」は何であるかあなたがそれを使用する方法 1または別の文脈で。

バーは、現実世界(またはゲームのような人工世界)の単一のエンティティであり、1つのオブジェクトインスタンスのみがそれを表す必要があります。後で、コードセグメントからそのインスタンスを作成せずに、構成ファイルまたはデータベースからロードすると、これはより明白になります。

(さらに難解なことに、各Barインスタンスには、プログラムの実行時にそれを表すオブジェクトとは異なるライフサイクルがあります。そのインスタンスを作成するソースコードがある場合でも、説明されているBarエンティティは存在します。 「ソースコードで休止状態にあり、そのコードが実際にメモリに作成したときに「目覚めます」...)

長いスタートで申し訳ありませんが、これが私のポイントを明確にすることを願っています。必要なすべての属性を持つ1つのBar クラスと、各Barエンティティを表す1つのBar インスタンスがあります。これはコードで正しく、異なるコンテキストで同じインスタンスを表示する方法とは無関係です

後者は、必要なアクセスメソッド(getName()、getURL()、getDistance())を含む2つの異なるインターフェイスで表すことができ、Barクラスは両方を実装する必要があります。(そしておそらく「距離」は「場所」に変わり、getDistance()は別の場所からの計算になります:-))

ただし、作成はBarエンティティ用であり、そのエンティティの使用方法(1つのコンストラクター、すべてのフィールド)ではありません。

編集:コードを書くことができます!:-)

public interface Place {
  String getName();
  Address getAddress();
}

public interface WebPlace extends Place {
   URL getUrl();
   Image getLogo();
}

public interface PhysicalPlace extends Place {
  Double getDistance();
  Slogon getSlogon();
}

public class Bar implements WebPlace, PhysicalPlace {
  private final String name;
  private final Address address;
  private final URL url;
  private final Image logo;
  private final Double distance;
  private final Slogon slogon;

  public Bar(String name, Address address, URL url, Image logo, Double distance, Slogon slogon) {
    this.name = name;
    this.address = address;
    this.url = url;
    this.logo = logo;
    this.distance = distance;
    this.slogon = slogon;
  }

  public String getName() { return name; }
  public Address getAddress() { return address; }
  public Double getDistance() { return distance; }
  public Slogon getSlogon() { return slogon; }
  public URL getUrl() { return url; }
  public Image getLogo() { return logo; } 
}

1

適切なパターン

あなたが探しているものは、最も一般的に呼ばれていNull Object Patternます。名前が気に入らない場合はUndefined Value Pattern、同じセマンティクスの異なるラベルと呼ぶことができます。このパターンは時々呼ばれPoison Pill Patternます。

これらすべてのケースでは、オブジェクトは、交換があるかのためにスタンドDefault Valueの代わりにnull. It doesn't replace the semantic ofnullをbut makes it easier to work with the data model in a more predictable way because今すべきnull`なので決して有効な状態になることはありませ。

これは、指定されたクラスの特別なインスタンスを予約して、それ以外のnullオプションをとして表すパターンですDefault Value。このようにして、確認する必要はありません。null既知のNullObjectインスタンスに対してIDを確認できます。について心配することなく、そのメソッドなどを安全に呼び出すことができますNullPointerExceptions

このようにして、null割り当てを代表的なNullObjectインスタンスに置き換えて完了です。

適切なオブジェクト指向分析

この方法によりInterface、ポリモーフィズムの共通点を持ちながら、インターフェイスの特定の実装にデータがないことを心配する必要がなくなります。そのBarため、Webに存在しないものもあれば、建設時に位置データを持たないものもあります。Null Object Patterこれらのそれぞれに対してデフォルト値を提供できます。markerこれは、同じことを言うデータの場合であり、ここでは何も提供されていませんNullPointerException

適切なオブジェクト指向設計

最初に、abstract両方BarClub共有のすべての属性のスーパーセットである実装を用意します。

class abstract Establishment 
{
     private final String name;
     private final Distance distance;
     private final Url url;

     public Bar(final String name, final Distance distance, final Url url)
     {
          this.name = name;
          this.distance = distance;
          this.url = url;
     }

     public Bar(final String name, final Distance distance)
     {
          this(name, distance, Url.UNKOWN_VALUE);
     }

     public Bar(final String name, final Url url)
     {
          this(name, Distance.UNKNOWN_VALUE, url);
     }

     // other code
}

そして、あなたはこのサブクラス実装できるEstablishmentクラスを、あなたがそれぞれのために必要なだけの固有のものを追加Barし、Club他には適用されませんクラスを。

持続性

これらのプレースホルダーオブジェクトは、正しく構成されていれば、特別な処理を行わなくてもデータベースに透過的に保存できます。

将来の証明

Inversion of Control / Dependency Injectionのバンドワゴンを後で使用することにした場合、このパターンにより、これらのマーカーオブジェクトも簡単にインジェクトでき​​ます。


0

問題は、どちらのシナリオでもバーをモデリングしていないことだと思います(そして、2つの異なる問題、オブジェクトなどをモデリングしています)。クラスバーが表示された場合、飲み物、メニュー、利用可能な座席に関連する機能が期待されますが、オブジェクトにはそれがありません。オブジェクトの動作を見ると、施設に関する情報をモデル化しています。現時点では、バーはそれらを使用しているものですが、それらが実装している本質的な動作ではありません。(別のコンテキスト:結婚をモデル化する場合、2つのインスタンス変数Person wife; Person husband; wifeは現在そのオブジェクトに与えている現在のロールですが、オブジェクトはまだPersonです)。私は次のようなことをします:

class EstablishmentInformation {
     private final String name;

     public EstablishmentInformation(String name){
          this.name = name;
     }

     // getters
}

class EstablishmentLocationInformation {
    EstablishmentInformation establishmentInformation;
     private final Distance distance;

     public EstablishmentLocationInformation (String name, Distance distance){
          this.establishmentInformation = new EstablishmentInformation(name)
          this.distance = distance;
     }
}

class EstablishmentWebSiteInformation {
    EstablishmentInformation establishmentInformation;
     private final Url url;

     public EstablishmentWebSiteInformation(String name, Url url){
          this.establishmentInformation = new EstablishmentInformation(name)
          this.url = url;
     }
}

-1

これを複雑にする必要はありません。2種類のオブジェクトが必要ですか?2つのクラスを作成します。

class OnlineBar {
     private final String name;
     private final Url url;
     public OnlineBar(String name, Url url){
          this.name = name;
          this.url = url;
     }

     // ...
}
class PhysicalBar {
     private final String name;
     private final Distance distance;
     public PhysicalBar(String name, Distance distance){
          this.name = name;
          this.distance = distance;
     }
     //...
}

それらを均等に操作する必要がある場合は、インターフェースを追加するか、リフレクションを使用することを検討してください。


@デビッド:ああ。共通のデータメンバが1つあります。緊急!
DeadMG

共通のインターフェースがなければ、このソリューションにはポリモーフィズムはありません。これらのクラスはどちらも、この不十分な設計決定で他のものに置き換えることはできません。それらが同じ属性のスーパーセットを持たないということではなく、デフォルトでそれらの属性のいくつかがであるということですnull。覚えているnullとは、属性がないことではなく、データがないことを意味します。

1
@DeadMGこれは、おそらく共有値を親オブジェクトにグループ化するという考えの演習です。そのコンテキストで提案した場合、ソリューションは完全な信用を得られません。
デビッドカウデン

OPは、代替可能性の必要性を指定していません。そして、私が言ったように、必要に応じてインターフェイスを追加したり、リフレクションを使用したりできます。
DeadMG

@DeadMGしかし、リフレクションは、1つの環境を使用してクロスプラットフォームモバイルアプリを記述しようとするようなものです。動作する可能性はありますが、正しくありません。リフレクションを使用してメソッドを呼び出す場合のペナルティは、通常のメソッド呼び出しよりも2〜50倍遅くなります。リフレクションは万能ではありません...
デヴィッド・コーデン

-1

このタイプの問題を抱える人に対する私の答えは、それを管理可能なステップ分解することです

  1. 最初に2つのクラスBarOneを作成し、BarTwo(またはそれらを両方ともBar異なるパッケージで呼び出します)
  2. 現時点ではコードの重複を心配しないで、個別のクラスとしてオブジェクトの使用を開始してください。あるものから別のものへのクロスオーバー(メソッドの重複)が発生すると気づくでしょう
  3. あなたはそれらが決して関連していないことを知るかもしれないので、あなたは彼らが実際barに問題のクラスをそれが表すものに名前を変更しない限り本当に両方であるか自問する必要があります
  4. 共通のフィールドまたは動作が見つかった場合は、interfaceまたはsuperclass共通の動作を抽出することができます
  5. あなたはたらinterfacesuperclass、あなたが作成することができbuilderたりfactory、あなたの実装オブジェクトを取得/作成します

(4と5は、この質問に対する他の回答の内容です)


-2

名前住所を持つLocationなどの基本クラスが必要です。これで、2つのクラスBarBarPreviewが基本クラスLocationを拡張しました。各クラスで、スーパークラスの共通変数を初期化してから、一意の変数を初期化します。

public class Location {
    protected final String name;
    protected final String address:

    public Location (String locName, String locAddress) {
    name = locName;
    address = locAddress
    }

}

public class Bar extends Location {
    private final int dist;
    private final String slogan;

    public Bar(String barName, String barAddress,
               int distance, String barSlogan) {
    super(locName, locAddress);
    dist = distance;
    slogan = barSlogan;
    }
}

BarPreviewクラスについても同様です。

夜の睡眠が良くなる場合は、コード内のLocationのすべてのインスタンスをAnythingYouThinkWouldBeAnAppropriateNameForAThingThatABarExtendsSuchAsFoodServicePlace -ffsに置き換えます。


OPはインスタンスが不変であることを望みます、それはすべてがそうでなければならないことを意味しますfinal

1
これは機能する可能性がfinalあります。フィールド@David が必要です。Barならないextend Locationことは意味がありませんが。おそらくBar extends BaseBarBarPreview extends BaseBarこれらの名前もあまり良い音ではないかもしれませんが、もっとエレガントなものを望んでいました
Blundell

@JarrodRoberson私は彼のためにそれをスケッチしているだけです。OPの質問の根本的な問題は、基本クラスと、基本クラスを拡張する2つの別個のクラスを持つ方法を知らないことです。私は単にそれを詳述しています。
デビッドカウデン

@Blundell世界の何について話しているの?バーが ある場所。LocationをBaseBarに置き換えるだけで、まったく同じものになります。あるクラスが別のクラスを拡張する場合、そのクラスがBase [ClassThatWillExtend]という名前である必要はないことを知っていますか?
デビッドカウデン

1
@DavidCowden、他のすべての回答の下にコメントとしてあなたの回答を宣伝するのをやめてもらえますか?
マルクタニ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.