2015-01-10 2 views
4

instanceof를는의는 다음과 같은 상황을 가정 해 봅시다 : 나는복합 패턴과

내가하고 (대한 getName()이있다) "BidComponent"와 같은 추상적 인 슈퍼 클래스를 만들 복합 디자인 패턴으로 입찰 응용 프로그램 (이베이 등)을 설계 할 두 개의 하위 클래스 "Article"과 "Category".

카테고리에는 다른 BidComponents를 포함 할 수있는 목록이 있지만 Article은 List가 아니라 getPrice() 메소드를 구현합니다. 나는이 구조를 반복 할 내가 범주 - 제-구조를 인쇄하려면

내가 instanceof를 필요 : 이것은 나에게 매우 잘못된 것

if(element instanceof Article){ 
    Article article = (Article)element; 
    System.out.println(article.getName() + ":" + article.getPrice()); 
}else{ 
    Category category = (Category)element; 
    System.out.println(category.getName()); 
} 

. 이것을 실현하는 더 좋은 방법이 있습니까 (instanceof를 통해 항상 유형을 확인하지 않고)? 내가 instanceof를를 사용하는 것이 나쁜 디자인이라고 여러 번 읽기 때문에

// 편집 방문자와 내 문제를 언급 ...이 질문 :

좋아. 그러나 모든 제품에 대해 가장 높은 입찰가를 검색하려고한다고 가정 해 봅시다. 그래서 내가 가지고있다

public class HighestBidVisitor implements BidComponentVisitor{ 
    private double highestBid = 0d; 

    public HighestBidVisitor(Category category){ 
     visitCategory(category); 
    } 

    @Override   
    public void visitCategory(Category category){ 
     Iterator<BidComponent> elementsIterator = category.iterator(); 
     while(elementsIterator.hasNext()){ 
      BidComponent bidComponent = elementsIterator.next(); 

      //Now I have again the problem: I have to check if a component in the Categorylist is an article or a category 
      if(bidComponent instanceof Article) visitArticle((Article)bidComponent); 
      else visitCategory((Category)bidComponent); 

     } 
    } 

    @Override 
    public void visitArticle(Article article){ 
     if(article.getPrice() > highestBid) highestBid = article.getPrice(); 
    } 


} 

그러나 지금 나는 같은 문제 (visitCategory의 코멘트를 보라)를 다시 가지고있다. 아니면 내가 잘못하고있는거야?

답변

2

visitor pattern을 사용하고 싶습니다. 컴포지트 및 방문자 패턴은 종종 함께 작업

public abstract void visitChildren(BidComponentVisitor visitor); 

:

public interface BidComponentVisitor { 

    void visitArticle(Article article); 

    void visitCategory(Category category); 
} 

는 그런 다음 BidComponent 클래스는 방문 방법이있을 것입니다.

편집 :을 vistor 패턴을 당신이 visitChildren 메소드를 구현하는 방법입니다 사용할 때 instanceof을 피하는 키입니다. Category에서는이처럼 구현하는 것이 : Article 이후

@Override 
public void visitChildren(BidComponentVisitor visitor) { 
    vistor.visitCategory(this); 
    for (BidComponent child : children) { 
    child.visitChidren(visitor); 
    } 
} 

에 아이가없는, 그 구현이 간단 것 :

@Override 
public void visitChildren(BidComponentVisitor visitor) { 
    vistor.visitArticle(this); 
} 

그들은 키가 복합 패턴의 각 콘크리트 클래스가 자신의 유형 알고있다, 그래서 특정 유형의 매개 변수가있는 특정 방문자 메소드를 호출 할 수 있습니다.

하나의 변화는 입력하는 것입니다 자녀가있는 모든 클래스에 대한 방문자의 종료 방법 : 위의 인터페이스와

public interface BidComponentVisitor { 

    void visitArticle(Article article); 

    void enterCategory(Category category); 

    void exitCategory(Category category); 
} 

, Category.visitChildren()는 다음과 같이 보일 것이다 :

@Override 
public void visitChildren(BidComponentVisitor visitor) { 
    vistor.enterCategory(this); 
    for (BidComponent child : children) { 
    child.visitChidren(visitor); 
    } 
    vistor.exitCategory(this); 
} 

은을 인쇄하려면 나무, 당신은 이런 식으로 할 수 :

public class PrintingVisitor implements BidComponentVisitor { 
    private int depth = 0; 

    private void printIndent() { 
    for (int i = 0; i < depth; i++) { 
     System.out.print(" "); 
    } 
    } 

    public void visitArticle(Article article) { 
    printIndent(); 
    System.out.println(article.toString()); 
    } 

    public void enterCategory(Category category); 
    printIndent(); 
    System.out.println(category.toString()); 
    depth++; 
    } 

    public void exitCategory(Category category) { 
    depth--; 
    } 
} 

Th 방문자 패턴의 단점은 방문자 클래스가 가능한 모든 하위 클래스를 하드 코딩하거나 일반 visitOther() 메서드를 필요로한다는 것입니다.

+0

이 나에게 너무 복잡한 것 같다. 스트링을 돌려주는 getDescription() 메소드를 만들지 않고 자체 구현으로 두 클래스에서 오버라이드 (override)하는 것이 어떨까요? –

+0

@ABoschman 당신이 필요로하는 것이'getDescription()'이라면 당신의 솔루션은 작동 할 것입니다. (답을 자유롭게 추가하십시오.)하지만 더 복잡한 것이 필요하다면 도움이되지 않을 것입니다. 방문객은 귀하의 디자인을 확장을 위해 열어 주지만 수정을 위해 닫을 수 있습니다. – NamshubWriter

+0

답변 해 주셔서 감사합니다. 귀하의 솔루션에 대한 문제점을 설명하는 첫 번째 게시물을 편집했습니다 ...이 문제를 해결할 수 있도록 도와 주시겠습니까? – Sotnem

1

나는 지금 어떤 깨끗한 해결책도 생각할 수 없다. 구현을 업데이트하여 ArticleCategory 인스턴스를 별도로 저장해야 할 수도 있습니다. List<BidComponent>이 통과되어야하며 각 요소는 그것의 종류에 따라 처리해야 현재 구현

,이 방법은 조금 더 나을 수 있습니다

abstract class BidComponent { 
    public abstract String process(); 
} 

class Category extends BidComponent { 
    @Override 
    public String process() { 
     return getName(); 
    } 
} 

class Article extends BidComponent { 
    @Override 
    public String process() { 
     return getName() + " " + getPrice(); 
    } 
} 


List<BidComponent> list = ..; 
for (BidComponent c : list) { 
    System.out.println(c.process()); 
} 

또 다른 방법에서 처리 로직을 분리하는 클래스/객체는이 자바 8 람다를 사용하지만 동일 (Function 유사) 자신의 인터페이스를 사용하여 자바 7 이하로 구현 될 수있다 또는 사람이 0에 의해 제공되는 것을

Map<Class<?>, Function<BidComponent, String>> processors = new HashMap<>(); 
processors.put(Category.class, Category::getName()); 
processors.put(Article.class, a -> a.getName() + " " + a.getPrice()); 

List<BidComponent> list = ..; 
for (BidComponent c : list) { 
    System.out.println(processors.get(c.getClass()).apply(c)); 
} 

주또는 Apache Commons.

2

귀하는 방문자 구현을 잘못하고 있습니다. 다른 컴퍼넌트는 독자적인 요소의 발송을 처리합니다. 그들은 어떤 유형인지 알기 때문에 어떤 instanceof 점검도 할 필요가 없습니다.

public interface Visitor{ 
    void visit(Article a); 

    void visit(Category c); 

} 

abstract class BidComponent{ 
    ... 
    abstract void accept(Visitor v); 
} 

public class Category{ 

    .... 
    public void accept(Visitor v){ 
     v.visit(this); // visit Category 
     for(Article a : getArticles()){ 
     v.visit(a); //visit each article 
     } 
    } 
} 

그런 다음 방문자 모두를 검색 그런 다음 가장 높은 입찰

public class HigestBidVisitor implements Visitor{ 
    private final double highest; 

    void visit(Category c){ 
     //no-op don't care 

     //or we could track which Category we have visited last 
     //to keep track of highest bid per category etc 
    } 

    void visit(Article a){ 
     highest= Math.max(highest, a.getPrice());   
    } 
} 

를 찾을 수 있습니다 :

내가 동의
HigestBidVisitor visitor = new HighestBidVisitor(); 

BidComponent root = ... 

root.accept(visitor); 
double highest = visitor.getHighestPrice();