铸造接口

时间:2010-04-12 10:33:41

标签: c# java interface

接口提供了有用的抽象功能。可以有一个类Foo实现一些接口,例如A,B和C.一些客户端代码可以获得类型A的引用,其他类型的类型B等等。每个实际上都是相同的Foo对象但是接口只暴露一个狭窄的功能的子集。当然,邪恶的客户端代码可以尝试将A引用转换为Foo,然后访问其他功能。如何防止这种情况?

7 个答案:

答案 0 :(得分:10)

这称为“恶意转换”,您可以通过使用仅实现您想要公开的窄接口的包装器来阻止它(通过委托给您将直接传递给邪恶的对象的私有引用客户端)。

然而,如果客户端不仅是邪恶的,而且也是强大的,他或许可以使用反射来获取隐藏的引用。

答案 1 :(得分:2)

正常继承将始终允许它,您无法使用它。如果您想将某些类作为接口公开但隐藏其他方法,请使用Adapter pattern(google it)

答案 2 :(得分:1)

你做不到。一种解决方法是实现三个代理类,一个用于实现每个接口,将所有调用转发到单个Foo实例。

答案 3 :(得分:1)

执行恶意投射的人自行承担风险。在几乎所有情况下,您都可以安全地假设用户不会以指定接口协定之外的方式使用对象。

您真正需要使用代理对象的唯一情况是您将安全敏感对象暴露给不受信任的代码。否则,花时间制作关于如何使用对象的明确文档,并假设它将被遵循。

答案 4 :(得分:0)

隐藏基础对象。

假设你有:

public interface A {
}

public class B implements A {
}

因此,接口A只实现了B功能的一个子集。实际上,它隐藏了B的部分内容。您的问题是如何阻止用户向下转发A到B。

B objectOfTypeB = (B)objectOfTypeA; // you don't want this

因此,不允许用户访问B类。如果用户无法导入它,则无法将其实例化或向下传播。所以,他强迫使用界面,仅此而已。

将上述代码更改为:

/* Publicly accessable interface */
public interface A {
}

/* Class hidden inside the package. */
public class B implements A {
}

然后,你可以让一个函数返回A,确保用户不能使用B。

/* Function that returns an A. */
public A foo() {
    /* ... */
    return objectOfTypeB;
}

答案 5 :(得分:0)

您可以使用Facade类。

这个类应该包装一个类Foo的委托,然后只暴露接口方法,比如A,然后将它们转发给委托。

另一方面,您可以通过将包声明为私有来防止强制转换为Foo,并且具有仅返回接口A(实际上是Foo)的公共工厂方法。那种方式从其他包中进行转换是不可能的(仍然有人可能会用反射来玩弄。)

答案 6 :(得分:0)

没有真正实用的,非侵入性的方法可以防止这种情况发生。

然而,如果您的情况确实需要这种保护,请使用此实用程序类创建动态代理(委托)类(改编自Dynamic Proxy Classes - < 50行生产代码!! )。

如果某人使用了恶意演员,那么这会在运行时导致ClassCastException。您甚至可以将代码条件化为在生产时将其关闭(让newInstance()只返回obj - 将对象作为“代理”)。

<强> DynamicProxy.java

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class DynamicProxy implements java.lang.reflect.InvocationHandler {

    private Object obj;

    public static Object newInstance(Object obj, Class<?>... interfaces) {
        if (interfaces == null || interfaces.length == 0) {
            throw new IllegalArgumentException("No interfaces");
        }
        return java.lang.reflect.Proxy.newProxyInstance(
            obj.getClass().getClassLoader(),
            interfaces,
            new DynamicProxy(obj));
    }

    private DynamicProxy(Object obj) {
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method m, Object[] args)
    throws Throwable
    {
        Object result;
        try {
            result = m.invoke(obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " +
                           e.getMessage());
        }
        return result;
    }

    // ** DEMO CODE BELOW HERE **

    interface A {
        void methodA();
    }

    interface B {
        void methodB();
    }

    static class Foo implements A, B {
        public void methodA() { System.out.println("A"); }
        public void methodB() { System.out.println("B"); }
    }

    public static void main(String[] args) {

        Foo foo = new Foo();  // implements both interfaces

        // calls foo's methods, but only A methods
        A a = (A) DynamicProxy.newInstance(foo, A.class);

        // calls foo's methods, but only B methods
        B b = (B) DynamicProxy.newInstance(foo, B.class);

        // calls foo's methods, but only B methods
        A ab = (A) DynamicProxy.newInstance(foo, A.class, B.class);

        a.methodA();
        b.methodB();
        ab.methodA();
        ((B) ab).methodB();

        // ClassCastException: $Proxy0 cannot be cast to DynamicProxy$Foo
        ((Foo) a).methodA();

        // ClassCastException: $Proxy1 cannot be cast to DynamicProxy$Foo
        ((Foo) b).methodB();

        // ClassCastException: $Proxy0 cannot be cast to DynamicProxy$B
        ((B) a).methodB();

        // ClassCastException: $DynamicProxy1 cannot be cast to DynamicProxy$A
        ((A) b).methodA();
    }
}