如果我们使用工厂方法,我们必须将创建的实现作为已实现接口的类型返回。
public class Factory {
public Product getProduct() {
return new ProductA();
}
}
public interface Product {
}
class ProductA implements Product {
}
为了避免客户能够将退回的产品投射到产品{A,B,C ...等}的具体实施中,我们必须:
com.example.client
和com.example.factory
) package com.example.client;
...
public class Client {
public static void main(String[] args) {
Product i = new Factory().getProduct();
ProductA a = (ProductA) i; // the type of ProductA isn't visible.
}
}
例如,我们需要使用隐藏方法
的同一工厂public class Factory {
public Product getProduct() {
return new ProductA();
}
Product[] getCreatedProducts() {
...
}
}
我在这里看到两个问题:
答案 0 :(得分:2)
我真的不明白为什么要将工厂和类放在单独的包中。
我通常在同一个包中创建公共接口,公共工厂类和包受保护的实现。因此,客户端可以仅使用工厂创建实例,并且不能向下转换,因为从其他包中看不到具体类。
答案 1 :(得分:2)
我没有看到两个包的优势。我建议这个替代方案:
package com.example.client ;
public interface Product
{
/* stuff */
}
package com.example.client ;
public interface ProductFactory
{
Product make ( X1 x1 , X2 x2 , /* parameters */ , Xn xn ) ;
}
package com.example.manager;
interface ManagedProduct extends com.example.client.Product
{
/* management methods */
}
package com.example.manager ;
public final class DefaultProductFactory implements com.example.client.ProductFactory
{
public static final DefaultProductFactory instance = new DefaultProductFactory ( ) ;
private DefaultProductFactory ( )
{
super ( ) ;
}
public ManagedProduct make ( final X1 x1 , final X2 x2 , /* parameters */ , final Xn xn )
{
return new ManagedProduct ( )
{
/* implementation logic here */
} ;
}
/*
possibly other methods
The Product implementation class is invisible.
*/
}
答案 2 :(得分:2)
“默认”访问并不保证任何东西,因为任何流氓程序员都可以在你的包中声明他们的类。另外,无论你的包结构如何,在java中,你几乎总是可以做一个“实例”检查,然后向下转换为“实例”类型。因此,如果您的目标是阻止任何向下转发,则必须使用private
关键字。例如,您可以将Product
接口的具体实现声明为private static
,或将Factory
声明为匿名内部类。事实上,在布洛赫的“如何设计一个好的API”文章中,他指出你应该“尽量减少一切的可访问性。”
那就是说,我觉得你在这里有点偏执。如果有人垂头丧气,对你来说真的很重要吗?您编写的任何代码都可能被滥用,当然如果您包含一个记录良好的工厂,那么您已经提供了有关如何正确使用API的明确信息。此外,如果你构建一个真正的工厂方法,它接受参数并且具有清晰的方法名称,而不是这个没有参数的玩具Factory
例子,那么我认为你会发现你正在广播与公共相关的部分无论如何,正在创造什么。
答案 3 :(得分:1)
在这种情况下,您的客户端知道知道实现类的工厂。如果它们都在同一个进程中,那么客户端和实现类都被加载到同一个进程中,这意味着客户端可以通过反射访问实现类的底层方法。这假设您无法完全控制客户端运行时,即采取措施防止反射。但是,如果你这样做了,那么你可能不需要担心客户端无法转换为实现类。
因此,如果您将此视为针对不受信任的客户端流程的潜在安全机制,那么我不会对此抱有任何信任。如果您可以控制客户端,那么这可能足以让错误的程序员无意中发生混乱。