2013-06-06 20 views
3

我想用CDI實現工廠模式。這裏我們有商業案例:CDI的工廠模式取決於運行時參數

客戶端提供一個表示類型的字符串。根據這種類型,工廠返回一個接口的實現。

我知道有很多關於工廠模式和CDI的問題。我在這裏的區別是我解決了基於運行時參數的工廠返回的實現。

我正在考慮使用生產者方法,但後來我想不出如何將解析的實現注入到需要實現的bean中,因爲這是一個運行時參數,它不一定在建立時就知道。

所以我想到了使用Instance類的非常簡單的方法。

以下是基本實現:

// the interface. Instances of this class are returned from the factory 
public interface Product { 
} 

// one implementation may be returned by the factory 
@ProductType("default") 
public class DefaultProduct implements Product { 
} 

// another implementation may be returned by the factory 
@ProductType("myProduct") 
public class MyProduct implements Product { 
} 

// the qualifier annotation 
@Qualifier 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.FIELD, ElementType.TYPE}) 
public @interface ProductType { 
    String value(); 
} 

// the Annotation implementation to select 
// the correct implementation in the factory 
public class ProductTypeLiteral extends AnnotationLiteral<ProductType> 
    implements ProductType { 

    private String type; 

    public ProductTypeLiteral(String type) { 
     this.type = type; 
    } 

    @Override 
    public String value() { 
     return type; 
    } 
} 

// the factory itself. It is annotated with @Singleton because the 
// factory is only needed once 
@Singleton 
public class Factory { 

    @Inject 
    @Any 
    private Instance<Product> products; 

    public Product getProduct(String type) { 
     ProductTypeLiteral literal = new ProductTypeLiteral(type); 
     Instance<Product> typeProducts = products.select(literal); 
     return typeProducts.get(); 
    } 
} 

在使用實例我看來是非常複雜的。 但是這有一個主要的缺點: 每次你打電話Instance.get()方法你檢索Product新的實例。這可能沒問題,但Instance實例在內部保留了返回實例的引用。所以只要Factory有效並且每次調用Instance.get()時,內存中就會存在更多的Product實例,並且永遠不會收集垃圾,因爲引用仍然保留在Instance中。

我想沒有使Factory單身,但只是轉移問題,並沒有解決它。當然這是違反工廠模式的。

我嘗試另一種解決方案是通過Instance迭代,而不是與註釋的幫助下選擇執行:

@Singleton 
public class Factory { 

    @Inject 
    @Any 
    private Instance<Product> products; 

    public Product getProduct(String type) { 
     Product product = null; 
     for(Product eachProduct : products) { 
      ProductType productType = eachProduct.getClass(). 
              getAnnotation(ProductType.class) 
      if(productType.value().equals(type) { 
       product = eachProduct; 
       break; 
      } 
     } 
     return product; 
    } 
} 

基本上,這是工作。現在每次取決於給定的類型,我檢索Product的相同實例。這樣內存不會被消耗。 但我不喜歡它迭代集合,當我有可能更加優雅地解決正確的實現。

你有什麼想法可以解決這個問題嗎?否則,我可能不得不保持迭代解決方案。

+1

不@辛格爾頓連做什麼嗎?另外,似乎你並沒有考慮到你要返回的東西在CDI中已經有了自己的範圍/上下文,在你的工廠裏緩存它們是沒有意義的,從長遠來看,它會受到限制。 – covener

+0

感謝您的評論!那麼我會說,在哪裏你注入'工廠'相同的實例使用。你說得對,我沒有緩存任何東西,但我的'Factory'中的'Instance'成員變量保存了get()方法返回的所有實例的引用。這是如何限制的? – Sancho

+0

我沒想過將它設置爲「@ RequestScoped」,因爲就像在老式的工廠模式中一樣,工廠只存在一次。但是,也許我會試一試,仔細看看CDI示波器。無論如何感謝@rdcrng! – Sancho

回答

1

這裏存在你的問題。實例一直使用get()從它獲得的實例參考,因爲它在超出範圍時負責回收它們(即當注入的Instance超出範圍時,但由於您使您的工廠成爲單例,所以它永遠不會退出因此,讓你的工廠成爲一個短暫的範圍,如@RequestScoped甚至@Dependent,這樣所有返回的實例將被正確回收。

0

也許它可以幫助你:

創建預選賽:

@Qualifier 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) 

public @interface MyProduct{ 

} 

@Qualifier 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) 

public @interface DefaultProduct{ 

} 

在工廠類:

@Singleton 
public class Factory { 

public Product getProduct(@MyProduct MyProduct product, @DefaultProduct DefaultProduct defaultProduct) { 
    //What you wanna do 
} 
}