어떤 OO 디자인을 사용해야합니까 (디자인 패턴이 있습니까)?

‘Bar / Club'(음료 / 사회적 장소)을 나타내는 두 가지 개체가 있습니다.

한 시나리오에서 막대 이름, 주소, 거리, 슬로건이 필요합니다.

다른 시나리오에서는 바 이름, 주소, 웹 사이트 URL, 로고가 필요합니다

그래서 같은 것을 나타내지 만 다른 필드를 가진 두 개의 객체가 있습니다.

불변의 객체를 사용하고 싶습니다. 그래서 모든 필드는 constructor에서 설정됩니다 .

하나의 옵션은 두 개의 생성자를 사용하고 다른 필드는 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
}

getter를 사용할 때 null 검사를해야하므로 이것을 좋아하지 않습니다.

실제 예제에서 첫 번째 시나리오에는 3 개의 필드가 있고 두 번째 시나리오에는 약 10 개의 필드가 있으므로 두 개의 생성자 가 있으면 고통 이 될 것 입니다. 필드를 null로 선언 해야하는 필드의 수는 객체가 사용 중일 때 ‘ Bar어떤 필드를 사용하고 어떤 필드가 null인지, 그렇지 않은 필드를 알지 못합니다.

다른 옵션이 있습니까?

두 클래스 BarPreviewBar?

어떤 유형의 상속 / 인터페이스?

다른 멋진 것이 있습니까?



답변

내 생각:

도메인에 표시되는 ‘바’에는 이름, 주소, URL, 로고, 슬로건 및 거리와 같은 장소에 필요할 수있는 모든 항목이 있습니다 (요청자의 위치에서 추측합니다). 따라서 도메인에는 나중에 데이터가 사용될 위치에 관계없이 하나의 막대에 대한 권한있는 데이터 소스 인 “Bar”클래스가 하나 있어야합니다. 이 클래스는 변경 가능해야 바의 데이터를 변경하고 필요할 때 저장할 수 있습니다.

그러나이 Bar 객체의 데이터가 필요한 두 곳이 있으며 둘 다 하위 집합 만 필요하며 해당 데이터를 변경하지 않으려는 경우가 있습니다. 일반적인 대답은 “데이터 전송 개체”또는 DTO입니다. 불변의 프로퍼티 게터를 포함한 POJO (plain ol ‘Java object) 이러한 DTO는 기본 Bar 도메인 오브젝트에서 “toScenario1DTO ()”및 “toScenario2DTO ()”메소드를 호출하여 생성 할 수 있습니다. 결과는 수화 된 DTO입니다 (즉, 길고 복잡한 생성자를 한곳에서 사용하면 됨).

데이터를 주 도메인 클래스로 다시 보내야하는 경우 (업데이트; 실제 세계의 현재 상태를 반영하기 위해 필요에 따라 데이터를 변경할 수없는 경우 데이터의 요점은 무엇입니까?) 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에서 사용될 때 변경 불가능한 클래스로 대체해야합니다.


답변

속성의 하위 집합 만 신경 쓰고 서로 혼동되지 않도록하려면 두 개의 인터페이스를 만들고이를 사용하여 기본 개체와 대화하십시오.


답변

빌더 패턴 (그것 또는 무언가 가까이) 여기에 사용 될 수 있습니다.

불변의 객체를 갖는 것은 훌륭한 일이지만 실제로는 Reflection in Java를 사용하면 실제로 안전한 것은 없습니다 ;-).


답변

여기서 중요한 점은 “바”의 정의 와 하나 또는 다른 컨텍스트에서 이를 사용하는 방법 의 차이점 입니다.

Bar는 실제 세계 (또는 게임과 같은 인공 세계)의 단일 엔티티이며 하나의 객체 인스턴스 만 표시해야합니다. 나중에 코드 세그먼트에서 해당 인스턴스를 만들지 않고 구성 파일 또는 데이터베이스에서 인스턴스를로드하면 더 분명합니다.

(매우 난해 해짐에 따라 : 각 Bar 인스턴스는 프로그램이 실행될 때이를 나타내는 오브젝트와 다른 라이프 사이클을 갖습니다. 해당 인스턴스를 작성하는 소스 코드가 있어도 설명 된 바와 같이 Bar 엔티티가 존재한다는 것을 의미합니다. “소스 코드에서 휴면 상태에 있고 해당 코드가 실제로 메모리에 생성 될 때”깨어 나면 “…)

오랫동안 시작해서 미안하지만, 이것이 내 요점을 분명히하기를 바랍니다. 필요한 모든 속성을 가진 하나의 Bar 클래스 와 각 Bar 엔티티를 나타내는 하나의 Bar 인스턴스가 있습니다. 이것은 코드에서 정확 하며 다른 컨텍스트에서 동일한 인스턴스를 보는 방법 과 무관 합니다 .

후자는 필요한 액세스 메소드 (getName (), getURL (), getDistance ())를 포함하는 두 개의 서로 다른 인터페이스 로 표시 될 수 있으며 Bar 클래스는 둘 다 구현해야합니다. (그리고 아마도 “거리”는 “위치”로 바뀌고 getDistance ()는 다른 위치에서 계산됩니다 :-))

그러나 생성은 Bar 엔티티를위한 것이며 해당 엔티티를 사용하려는 방식이 아닙니다. 하나의 생성자, 모든 필드.

편집 : 코드를 작성할 수 있습니다! 🙂

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대신 null. It doesn't replace the semantic ofbut 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는 웹에 존재하지 않을 수 있으며 일부는 생성 당시 위치 데이터가 없을 수 있습니다. 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클래스를하고 각각에 필요한 단지 특정 일 추가 BarClub다른 적용되지 않습니다 클래스를.

고집

이러한 자리 표시 자 개체는 올바르게 구성된 경우 특별한 처리없이 데이터베이스에 투명하게 저장할 수 있습니다.

미래의 증거

나중에 Inversion of Control / Dependency Injection bandwagon으로 넘어 가기로 결정한 경우이 패턴을 사용하면 이러한 마커 객체를 쉽게 주입 할 수 있습니다.


답변

문제는 두 시나리오 중 하나에서 막대를 모델링하지 않는다는 것입니다 (그리고 두 가지 다른 문제, 객체 등을 모델링하고 있습니다). 클래스 바가 보이면 음료, 메뉴, 좌석 및 개체와 관련된 기능이 없을 것입니다. 객체 동작이 표시되면 시설에 대한 정보를 모델링하는 것입니다. Bar는 현재 당신이 사용하고있는 것이지만, 그들이 구현하는 본질적인 행동은 아닙니다. (다른 맥락에서 : 결혼을 모델링하는 경우 두 가지 인스턴스 변수 Person wife; Person husband; 아내는 현재 해당 객체에 현재 역할을 부여하지만 해당 객체는 여전히 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;
     }
}

답변

과도하게 복잡 할 필요는 없습니다. 서로 다른 두 종류의 물체가 필요하십니까? 두 개의 수업을 만듭니다.

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;
     }
     //...
}

동일하게 작업해야하는 경우 인터페이스 추가 또는 리플렉션 사용을 고려하십시오.