【问题标题】:programming language with granular method and property access具有粒度方法和属性访问的编程语言
【发布时间】:2010-08-11 18:24:55
【问题描述】:

想象一下这样的事情:

import class B.*;


interface A supports A.testSum
{
   int sum( int a , int b ) access from B.calculator;

   testSum() { Assert(sum(1,1)==2); }

........


class B ...
{
  void calculator() {  A.sum(3,5); //ok }
  void someOtherMethod() { A.sum(0,3); //compile error }

“支持”的想法是次要的但相关的,因为在这种情况下测试适用于接口(因此语言会区分所有实现必须通过的接口测试和特定于实现私有

但我想在这里传达的重要思想是访问控制语义;请注意,带有“access from”关键字的 A.sum 只能从方法 B.calculator 中调用。其他任何东西都被检测为编译时错误。这里的想法是以更细化的方式实施架构约束。如果您没有添加“访问自”或仅添加“访问自 *”,则意味着允许从任何地方调用该方法的默认行为。什么样的架构约束?好吧,在进行分层设计时手动执行的那种:A层(最低层)从B层(中间层)使用,而B层又从C层(高层)使用。但是 B 层不能从 A 层访问,C 层也不能从 A 或 B 访问,但它是公共的(它可能是最终用户可以直接访问的)

问题:你知道支持上述语义的任何语言(包括源到源中间语言)吗?讨论这种语义是否会适得其反、危险或只是鼓励不良设计的额外要点

更新:这种限制还有另一个非常重要的用例:

事件驱动编程:通常事件的问题是事件往往做的太多,理解事件的依赖链可能会变得很棘手

因此,例如,可以定义事件处理程序只有一组可以交互的可见类(或者相反,它不能接触的一组对象)

【问题讨论】:

    标签: design-patterns architecture programming-languages access-control layered


    【解决方案1】:

    这听起来像是object-capability model 的一个特例。也许有些语言以某种方式实现了这一点。

    类似地,快速搜索一下“方法级安全性”让我发现了一些企业 Java 社区似乎已经准备好的东西。我认为将这种方法专门用于方法调用是毫无意义的。除非您有充分的理由这样做,否则我认为这可能是个坏主意。如果您出于某种原因真的有兴趣这样做,那么模型应该是让接收者检查调用源是否在某个允许的集合中。

    无论如何,这基本上严重破坏了大多数编程模型。最好强制执行前提条件和类不变量,以确保任何方法调用(从任何地方!)都是有意义的或行为良好的。如果您使用它来强制执行方法排序,则可以使用不变检查(静态或在运行时)或理论模型(例如 Interface Automata)来实现。

    【讨论】:

    • 有道理,按合同设计是一种更好、更通用的约束
    【解决方案2】:

    Java 支持的东西几乎相同。

    首先,字段和方法的可见性是在运行时强制执行的,非特权代码不可能绕过这一点。

    您还可以创建自己的权限,并将其授予某些代码部分。例如,要打开一个文件,想要访问一个文件的代码需要该文件的FilePermission。不过,您可以进行任何您希望的权限,也可以创建一个名为SumPermission 的权限,Calculator 在求和之前会对其进行检查,并且只将其授予您想要的任何类。保护域跨越类,而不是类中的单个方法,因为整个类通常是从单一来源获得的。该模型实际上比您提出的更深入。导致安全检查的堆栈上的每个类(包括线程创建的历史)都必须具有权限,因此如果某些不受信任的代码调用具有SumPermission 的代码,它将无法通过安全检查。当然这只是默认设置,每当你做任何需要权限的事情时,你都可以使用doPrivileged 块,告诉即将进行的检查只检查你的权限,而不是你和你的调用者的权限。

    但是,Java 中当前的默认安全方案有很多限制。一方面,不受信任的代码不能细分其权限或为嵌套的不受信任代码定义自己的权限。此外,防止不受信任的代码阻塞也是一种痛苦

    您可能想查看E。特别是,它遵循对象能力模型。它是为相互不信任的代码安全交互而设计的,并且具有语言级别的结构来防止死锁问题。

    在 Java 中实现相互不信任的代码之间的健壮行为是完全可能和可行的,但 E 可能会使您的工作更轻松,并且在 JVM 上运行,因此您应该仍然能够使用 Java 库和来自任何其他语言的库使用 JVM。

    【讨论】:

      【解决方案3】:

      这在 Ruby 中是可行的,尽管语法不同。采取以下措施:

      module T
          def check
              raise unless self.is_a?(Ca)
              raise unless %r{in `good_func'} =~ caller.first #`
              true
          end
      end
      
      class Ca
          include T
          def good_func
              check
          end
          def bad_func
              check
          end
      end
      
      class Cb
          include T
          def good_func
              check
          end
          def bad_func
              check
          end
      end
      
      a = Ca.new
      b = Cb.new
      
      a.good_func
      => true
      a.bad_func
      => (RuntimeError)
      
      b.good_func
      => (RuntimeError)
      b.bad_func
      => (RuntimeError)
      

      当使用模块作为混入时,self 对应于 includes 模块的类。 caller 返回当前调用堆栈,caller.first 为您获取调用堆栈上的第一个条目(即调用此条目的函数)。

      【讨论】:

      • 你必须原谅语法高亮,反引号把它扔掉了。第四行末尾的注释毫无意义,只是为了让语法高亮显示回到正轨。有没有更干净的方法来逃避那个角色?我忘记了所有的标记规则...
      • 语法差异是可以的,只要语义相同。但是,此检查会在运行时发生,对吗?所以我们每次调用函数时都会验证约束,但在编译时检测违规并避免引入不必要的运行时开销会更令人满意
      • 我认为,如果一个函数只能由特定函数调用,那么它也应该作为该函数的逻辑子部分存在。要么将其设为仅存在于calculator 范围内的私有子函数,要么将calculator 设为具有公共calculate 方法和私有sum 方法的对象。
      • @lurscher- 函数不一定能在编译时确定谁在调用它(尤其是在使用函数指针的情况下)。将函数 A 限制为函数 B 的最简单方法是限制函数 A 的范围。如果函数 A 仅存在于函数 B 内部,则根据定义,它不能在其他地方调用。 C/C++ 不支持本地函数,但它可以让你在函数 B 中为函数 A 放置一个 prototype。如果函数 A 在其他任何地方都没有原型,那么它只能在函数内部访问B.
      • 比“脆弱”或“耦合”更重要的是,我会说这种技术通过鼓励相关功能在单独的对象/功能之间传播来促进不良的设计习惯。您本质上是想要控制函数的范围。在大多数语言中,这是通过类和函数的布局和设计来完成的,而不是通过手动说明符。通过代码结构强制实施范围可确保一致且可预测地应用范围规则,并有助于促进封装和逻辑代码结构。
      猜你喜欢
      • 2017-06-16
      • 1970-01-01
      • 2011-03-30
      • 2015-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多