문제

두 개의 병렬 상속 체인이 있습니다.

Vehicle <- Car
        <- Truck <- etc.

VehicleXMLFormatter <- CarXMLFormatter
                    <- TruckXMLFormatter <- etc.

내 경험에 따르면 병렬 상속 계층 구조는 성장함에 따라 유지 관리에 골칫거리가 될 수 있습니다.

즉.추가하지 않음 toXML(), toSoap(), toYAML() 내 주요 수업에 대한 방법.

관심사 분리 개념을 깨지 않고 병렬 상속 계층 구조를 어떻게 피할 수 있나요?

도움이 되었습니까?

해결책

나는 방문자 패턴을 사용하려고 생각하고 있습니다.

public class Car : Vehicle
{
   public void Accept( IVehicleFormatter v )
   {
       v.Visit (this);
   }
}

public class Truck : Vehicle
{
   public void Accept( IVehicleFormatter v )
   {
       v.Visit (this);
   }
}

public interface IVehicleFormatter
{
   public void Visit( Car c );
   public void Visit( Truck t );
}

public class VehicleXmlFormatter : IVehicleFormatter
{
}

public class VehicleSoapFormatter : IVehicleFormatter
{
}

이를 통해 여분의 상속 트리를 피하고 서식 로직을 차량 클래스에서 분리합니다. Offcourse, 새 차량을 만들 때 Formatter 인터페이스에 다른 방법을 추가해야합니다 (Formatter 인터페이스의 모든 구현 에서이 새로운 방법을 구현).
그러나 나는 이것이 새로운 차량 클래스를 만드는 것이 더 좋다고 생각하며, 당신이 가진 모든 ivehicleformatter에 대해이 새로운 종류의 차량을 처리 할 수있는 새로운 클래스를 만듭니다.

다른 팁

또 다른 접근법은 풀 모델이 아닌 푸시 모델을 채택하는 것입니다. 일반적으로 캡슐화를 깨고 다음과 같은 것을 가지고 있기 때문에 다른 형식 반포가 필요합니다.

class TruckXMLFormatter implements VehicleXMLFormatter {
   public void format (XMLStream xml, Vehicle vehicle) {
      Truck truck = (Truck)vehicle;

      xml.beginElement("truck", NS).
          attribute("name", truck.getName()).
          attribute("cost", truck.getCost()).
          endElement();
...

특정 유형에서 Formatter로 데이터를 가져 오는 곳.

대신, 형식 공유 데이터 싱크를 생성하고 흐름을 반전시켜 특정 유형이 데이터를 싱크로 밀어 넣습니다.

class Truck  implements Vehicle  {
   public DataSink inspect ( DataSink out ) {
      if ( out.begin("truck", this) ) {
          // begin returns boolean to let the sink ignore this object
          // allowing for cyclic graphs.
          out.property("name", name).
              property("cost", cost).
              end(this);
      }

      return out;
   }
...

즉, 여전히 데이터를 캡슐화했으며 태그가 지정된 데이터를 싱크대에 공급하는 것입니다. 그런 다음 XML 싱크는 데이터의 특정 부분을 무시하고 일부를 재주문하고 XML을 작성할 수 있습니다. 내부적으로 다른 싱크 전략을 위임 할 수도 있습니다. 그러나 싱크대는 반드시 차량의 유형에 대해 신경을 쓸 필요는 없으며 일부 형식으로 데이터를 표현하는 방법 만 있습니다. 인라인 문자열 대신 인턴 된 글로벌 ID를 사용하면 계산 비용을 낮추는 데 도움이됩니다 (ASN.1 또는 기타 타이트 형식을 작성하는 경우에만 문제가 있습니다).

포맷터에 대한 상속을 피하려고 할 수 있습니다.간단히 VehicleXmlFormatter 그건 처리할 수 있어 Car에스, Truck에, ...재사용은 메소드 간의 책임을 나누고 좋은 파견 전략을 파악함으로써 달성하기 쉬워야 합니다.마법의 과부하를 피하십시오.포맷터의 이름 지정 방법은 최대한 구체적으로 지정하세요(예: formatTruck(Truck ...) 대신에 format(Truck ...)).

이중 파견이 필요한 경우에만 방문자를 사용하십시오.유형의 객체가 있을 때 Vehicle 실제 구체적인 유형을 모르고 XML로 형식을 지정하려고 합니다.방문자 자체는 포맷터에서 재사용을 달성하는 기본 문제를 해결하지 못하며 필요하지 않은 추가 복잡성을 초래할 수 있습니다.메서드별 재사용(잘라내기 및 디스패치)에 대한 위의 규칙은 방문자 구현에도 적용됩니다.

당신이 사용할 수있는 Bridge_pattern

브리지 패턴은 구현에서 추상화를 해체하여 둘이 독립적으로 다를 수 있습니다..

enter image description here

두 개의 직교 클래스 계층 (the 추출 계층 및 구현 계층 구조)는 구성을 사용하여 연결됩니다 (상속이 아님).이 구성은 두 계층 모두 독립적으로 변화하는 데 도움이됩니다.

구현은 결코 참조하지 않습니다 추출. 추상화에는 포함됩니다 구현 구성원으로서의 인터페이스 (구성을 통해).

예로 돌아 오는 것 :

Vehicle ~이다 추출

Car 그리고 Truck ~이다 정교함

Formatter ~이다 구현 자

XMLFormatter, POJOFormatter ~이다 콘크리트 이식기

의사 코드 :

 Formatter formatter  = new XMLFormatter();
 Vehicle vehicle = new Car(formatter);
 vehicle.applyFormat();

 formatter  = new XMLFormatter();
 vehicle = new Truck(formatter);
 vehicle.applyFormat();

 formatter  = new POJOFormatter();
 vehicle = new Truck(formatter);
 vehicle.applyFormat();

관련 게시물 :

브리지 패턴은 언제 사용합니까? 어댑터 패턴과 어떻게 다른가요?

ixmlformatter를 toxml (), tosoap ()와 함께 yaml () 방법으로 인터페이스로 만들고 차량, 자동차 및 트럭을 모두 구현하십시오. 그 접근법에 무슨 문제가 있습니까?

프레드릭스 대답에 제네릭을 추가하고 싶습니다.

public class Car extends Vehicle
{
   public void Accept( VehicleFormatter v )
   {
       v.Visit (this);
   }
}

public class Truck extends Vehicle
{
   public void Accept( VehicleFormatter v )
   {
       v.Visit (this);
   }
}

public interface VehicleFormatter<T extends Vehicle>
{
   public void Visit( T v );
}

public class CarXmlFormatter implements VehicleFormatter<Car>
{
    //TODO: implementation
}

public class TruckXmlFormatter implements VehicleFormatter<Truck>
{
    //TODO: implementation
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top