'Bar/Club'(飲み物/社交の場)を表す2つのオブジェクトがあります。
あるシナリオでは、バーの名前、住所、距離、ログオンが必要です
別のシナリオでは、バー名、アドレス、ウェブサイトのURL、ロゴが必要です
したがって、同じものを表すがフィールドが異なる2つのオブジェクトがあります。
私は不変オブジェクトを使用したいすべてのフィールドはコンストラクターから設定される。
1つのオプションは、2つのコンストラクターを持ち、他のフィールドをnullにすることです。
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つのコンストラクターを持つ実際の痛みとなります。オブジェクトが使用中の場合、どのBar
をどこで使用しているかがわからないため、どのフィールドがnullでどのフィールドがnullになるかはわかりません。
他にどのようなオプションがありますか?
BarPreview
およびBar
?という2つのクラス
ある種の継承/インターフェース?
他に素晴らしいものはありますか?
私の考え:
ドメインで表される「バー」には、名前、住所、URL、ロゴ、スローガン、および「距離」(要求者の場所から推測しています)のいずれかの場所で必要となる可能性のあるものがすべて含まれています。したがって、ドメインには、後でデータが使用される場所に関係なく、1つのバーの信頼できるデータソースである「Bar」クラスが1つ必要です。このクラスは変更可能である必要があります。これにより、バーのデータに変更を加え、必要に応じて保存できます。
ただし、このBarオブジェクトのデータが必要な場所は2つあり、どちらもサブセットのみが必要です(そのデータを変更したくない)。通常の答えは「データ転送オブジェクト」またはDTOです。不変のプロパティゲッターを含むPOJO(プレーンol 'Javaオブジェクト)。これらのDTOは、メインバードメインオブジェクトのメソッドを呼び出すことによって生成できます: "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で使用される場合は不変のものに置き換える必要があります。
プロパティのサブセットのみに関心があり、それらが混同されないようにしたい場合は、2つのインターフェースを作成し、それを使用してベースオブジェクトと通信します。
Builderパターン (またはそれに近いもの)は、ここで役立つかもしれません。
不変のオブジェクトを持つことは立派なことですが、現実には、JavaでのReflectionを使用しても、本当に安全なものはありません;-)。
ここでの重要な点は、「バー」とはとの使い方の違いです1つまたは別のコンテキスト。
バーは実世界(またはゲームのような人工世界)の単一のエンティティであり、1つのオブジェクトインスタンスのみがそれを表す必要があります。後で、コードセグメントからそのインスタンスを作成せずに、構成ファイルまたはデータベースからインスタンスをロードすると、これはより明白になります。
(さらに難解なことに、各Barインスタンスは、プログラムの実行時にインスタンスを表すオブジェクトとは異なるライフサイクルを持っています。そのインスタンスを作成するソースコードがある場合でも、説明されているとおりにBarエンティティが存在することを意味します"ソースコードで休止状態にあり、そのコードが実際にメモリ内に作成したときに"覚醒 "します...)
長いスタートで申し訳ありませんが、これが私のポイントを明確にしてくれることを願っています。 1つのバークラスには必要なすべての属性があり、1つのバーインスタンスは各バーエンティティを表します。これはあなたのコードでは正しく、異なるインスタンスで同じインスタンスをどのように表示したいかとは無関係です。
後者は、必要なアクセスメソッド(getName()、getURL()、getDistance())を含む2つの異なるinterfacesで表すことができ、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; }
}
あなたが探しているものは、最も一般的には Null Object Pattern
と呼ばれます。名前が気に入らない場合は、Undefined Value Pattern
、同じセマンティクスの異なるラベルと呼ぶことができます。このパターンはPoison Pill Pattern
と呼ばれることもあります。
これらすべてのケースで、オブジェクトはDefault Value
nullnull. It doesn't replace the semantic of
null`の代わりにbut makes it easier to work with the data model in a more predictable way because
の代わりになるか、代わりになりますnever有効な状態であること。
これは、特定のクラスの特別なインスタンスを予約して、そうでなければnull
オプションをDefault Value
として表すパターンです。このように、null
に対してチェックする必要はありません。既知のNullObject
インスタンスに対してIDをチェックできます。 NullPointerExceptions
を気にすることなく、そのメソッドなどを安全に呼び出すことができます。
このようにして、null
割り当てをそれらの代表的なNullObject
インスタンスに置き換えれば完了です。
このようにして、ポリモーフィズムに共通のInterface
を使用でき、さらに、インターフェースの特定の実装にデータがないことを心配する必要がないように保護できます。したがって、一部のBar
にはWebプレゼンスがない場合や、構築時にロケーションデータがない場合があります。 Null Object Patter
を使用すると、同じことを言うデータのmarker
であるこれらのそれぞれのデフォルト値を指定できます。ここでは何も提供されておらず、至る所でNullPointerException
のチェックに対処する必要があります。
最初に、abstract
とBar
の両方が共有するすべての属性のスーパーセットであるClub
実装を用意します。
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
クラスのそれぞれに必要な特定のものだけを追加できます。
これらのプレースホルダーオブジェクトは、正しく構築されていれば、特別な処理をしなくてもデータベースに透過的に保存できます。
制御/依存性注入の逆転のワゴンに後でジャンプすることに決めた場合、このパターンにより、これらのマーカーオブジェクトも簡単に注入できます。
問題は、これらのシナリオのいずれかでバーをモデル化していないことです(そして、オブジェクトなど、2つの異なる問題をモデル化しています)。クラスバーが表示された場合、飲み物、メニュー、利用可能な座席に関連するいくつかの機能が期待されますが、オブジェクトにはそれがありません。オブジェクトの動作を確認すると、施設に関する情報をモデル化していることになります。 Barは現時点でそれらを使用しているものですが、それが実装している本質的な動作ではありません。 (別のコンテキストでは、結婚をモデル化している場合は、2つのインスタンス変数Person妻、Person夫、妻がその時点でそのオブジェクトに与えている現在の役割ですが、オブジェクトは依然として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;
}
}