C# 界面的铸造

C# 界面的铸造,c#,java,interface,C#,Java,Interface,接口提供了有用的抽象功能。可以让一个类Foo实现一些接口,比如a、B和C。一些客户机代码可能会获得类型a的引用,其他的可能会获得类型B的引用,等等。每个实际上都是相同的Foo对象,但接口只公开了功能的一小部分。当然,邪恶的客户端代码可以尝试强制转换对Foo的引用,然后访问其他功能。如何防止这种情况发生?您不能。一种解决方法是实现三个代理类,一个实现每个接口,将所有调用转发到一个Foo实例。这称为“恶意强制转换”,您可以通过使用只实现要公开的狭窄接口的包装器来防止它(通过委托给对象的私有引用,否则

接口提供了有用的抽象功能。可以让一个类Foo实现一些接口,比如a、B和C。一些客户机代码可能会获得类型a的引用,其他的可能会获得类型B的引用,等等。每个实际上都是相同的Foo对象,但接口只公开了功能的一小部分。当然,邪恶的客户端代码可以尝试强制转换对Foo的引用,然后访问其他功能。如何防止这种情况发生?

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

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


但是,如果客户机不仅邪恶,而且功能强大,那么他可能仍然能够使用反射来访问隐藏的引用。

正常继承总是允许它,您对此无能为力。如果您想将某些类公开为接口,但隐藏其他方法,请使用
适配器
模式(google it)隐藏底层对象

假设你有:

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;
}

您可以使用Facade类

这个类应该包装一个Foo类的委托,然后只公开接口方法,比如a,并将它们转发给委托


另一方面,您可以通过声明Foo包为private并使用一个只返回接口a(实际上是Foo)的公共工厂方法来防止对Foo进行强制转换。这样就不可能从其他包进行强制转换(不过,有人可能会对反射进行戏弄).

没有真正实用的、非侵入性的方法来预防这种情况


但是,如果您的情况确实需要这种保护,请使用此实用程序类来创建动态代理(委托)类(改编自-执行恶意强制转换的人会自行承担风险。在几乎所有情况下,您都可以安全地假设用户不会以指定接口约定之外的方式使用对象


您真正需要使用代理对象的唯一时间是将安全敏感对象暴露于不受信任的代码中。否则,请花时间明确说明如何使用对象,并在假定对象将被遵循的情况下工作。

“您可以将Foo设为私有类或内部类”。客户端仍然可以强制转换到B或C(接口都是公共的)。此外,主项目中的其他类可能需要
Foo
,这意味着它必须是公共的。感谢您的评论。我应该坚持我最初的建议。客户端仍然可以强制转换到B或C,但他不应该访问(接口都是公共的)。如果客户端没有访问B的权限,他也不能强制转换到B。例如,在Java中,可以有一个返回B的工厂,其中B受包保护。为什么要使事情复杂化?您有一个返回a的函数。由于用户没有访问类B的权限,他不能使用它的任何内容。不需要工厂方法;范围界定是enough来解决这个问题。如何在包中隐藏
public
类?如果客户端类是“恶意的”,作者只需检查jar(包)的内容即可发现所有可用的公共类,他们可以实验。如果用户没有访问B,这意味着他必须调用一些为他创建B的东西-在这种情况下,我认为是“工厂方法”,否则他可以做新的COM.Foo.b.(),或将结果强制转换为com.foo.BAdd,并添加到一个
SecurityManager
,您可以减少和/或消除使用反射的可能性。仅使用包保护并不总是有效,因为OP的项目中可能有其他包,这些包需要访问此类。
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();
    }
}