如何使私有成员对象封装?

时间:2016-10-20 00:45:58

标签: scala clojure f# erlang

在Scala中键入private[this],我们可以使私有成员只能由该类的特定对象访问,并且同一个类的另一个对象无法访问它。如果可能的话,我们怎样才能在其他语言中实现这一点......特别是F#,Erlang和Clojure?

4 个答案:

答案 0 :(得分:6)

您所询问的基本上是面向对象的数据抽象抽象数据类型的数据抽象 ADT s,不要与代数数据类型混淆,代数数据类型通常也缩写为“ADT”。 (On Understanding Data Abstraction, RevisitedWilliam R. CookProposal for Simplified, Modern Definitions of "Object" and "Object Oriented"Object-Oriented Programming Versus Abstract Data Types。)

相同抽象数据类型的两个不同实例可以检查彼此的表示(但不是其他抽象数据类型的实例的表示),而对象可以< em> never 检查另一个对象的表示,即使该对象是相同类型的实例。 Cook将此属性识别为面向对象数据抽象(实际上是面向对象)的基本和定义特征,并将其称为 Autognosis (自我知识)。我更喜欢 Xenoagnosis (外国非知识)一词,由Glenn Vanderburg,Rick de Natale或已故的Jim Weirich,IIRC应用,因为它强调了两种方法之间的区别,即物体无法了解其他物体。

在Scala中,正如您所注意到的,可以通过private[this]访问修饰符实现面向对象的数据抽象。

class TestPrivate {
  private       val notReallyPrivate = 42
  private[this] val privateIMeanIt   = 23

  def blowUp(other: TestPrivate) = {
    other.notReallyPrivate
    other.privateIMeanIt
  }
}
// error: value privateIMeanIt is not a member of TestPrivate

在Erlang中,与Object相当的是一个Process,而Processes完全由语言语义封装(它们甚至有自己的垃圾收集内存)。毕竟,它们甚至可以生活在不同大陆的不同机器上,因此暴露表示只是不切实际。您向流程发送消息,并且流程在决定如何处理消息以及如何响应消息时完全自主。 Everything 基本上都是private[this]

在Clojure中,可以通过Closures实现面向对象的数据抽象。 (对于几乎所有带闭包和高阶子程序的语言都是如此,例如你也可以在Erlang中使用函数作为对象而不是进程。)想想ECMAScript:尽管它容易混淆地有一个叫做“object”的语言结构, ECMAScript对象实际上是使用函数实现的。你可以用同样的方式在Clojure中实现它们。面向对象的数据抽象形式也称为过程数据抽象,因为它不依赖于类型,而是依赖于将数据隐藏在过程接口(或功能接口)之后,如果你想要的话,它毕竟是可变性和副作用与OO正交)。 Cook(半开玩笑地)认为,因为λ演算只有函数,所有抽象都是函数式的,因此λ演算是第一个,最古老,最纯粹的OO语言 - 显然这意味着OO数据抽象必须在语言中紧密基于λ演算,如Lisp族。 (历史轶事:Scheme最初是为研究OO和Actors而设计的,他们只是在实现过程中注意到,用于消息发送和函数调用的解释器的代码路径是相同的。)

在Java或C♯等语言中,只使用interface作为类型,使用Type System和Programmer Discipline实现面向对象。 interface无法描述表示,因此,只要您确定(通过程序员规则,编码样式,代码评论,可能是静态分析工具),只有interface被用作类型(每个本地)变量,字段,方法参数,方法返回值,强制转换操作符,instanceof检查和泛型类型参数必须始终interface)和class es 用作工厂(允许出现类名的唯一位置直接位于new关键字旁边),然后您已实现面向对象的数据抽象。 (还有一些其他注意事项,最重要的是你不能使用参考平等。)

interface ITestPrivate {
  default int thisIsPublic() { return 0; }

  default void blowUp1(ITestPrivate other) {
    other.thisIsPublic();
    other.privateIMeanIt();
  }
}

class TestPrivate implements ITestPrivate {
  private int privateIMeanIt() { return 23; }

  void blowUp2(TestPrivate other1, ITestPrivate other2) {
    other1.notReallyPrivate(); // works
    other2.notReallyPrivate(); // doesn't
  }
}

F♯ supports interfaces as well开始,也可以在F♯中执行此

答案 1 :(得分:3)

F#有publicinternalprivate access control specifiers。 Scalas限制性更强private[this]限制对类型实例的访问而不是类型本身不能作为说明符使用,因此即使private(F#中限制最多)成员也是如此可以访问other实例的。 class let bindings虽然是实例私有的:

type Foo(x) =
    let totallyPrivate = x
    member private(*[this]*) __.Bar = x
    member this.Baz (other : Foo) =
        // other.Bar not accessible in Scala
        this.Bar + other.Bar + totallyPrivate
        // the field, constructor or member 'totallyPrivate' is not defined
        // other.totallyPrivate

答案 2 :(得分:1)

正如我对@JörgWMittag回答的评论所暗示的那样,我倾向于同意@ SamEstep的评论,即你想要做的事,codingXeno,在Clojure中很少有意义。不过,在Common Lisp或Scheme中,这是一个很好的练习,在Clojure中只是稍微难一点。以下是您所要求的简单说明:

(def getit-and-setit (let [x (atom 42)]
                       [(fn [] @x), (fn [new-x] (reset! x new-x))]))

这里我们定义了一个局部变量x,其值为“atom”,即保存可以用新值替换的值的东西。 (可以使用@deref提取原子的内容。可以使用reset!或其他一些有用的运算符替换它。)然后我们返回一个双元素向量。每个元素都是一个函数。 (通常不使用逗号,但允许使用逗号并使此代码更清晰。)

(def get-it (first getit-and-setit))
(def set-it! (second getit-and-setit))

这里提取了第一个函数并定义了一个新变量get-it以将函数作为其值。我们定义set-it!将另一个函数作为其值。

get-it返回原子的内容,即x的值:

(get-it) ;==> 42

set-it!用新值替换atom的内容:

(set-it! 5)
(get-it) ;==> 5

除了通过xget-it之外,无法访问set-it!x是“私人”。

Common Lisp或Scheme中更简单的原因是,在这些语言中,可以为普通局部变量赋予新值。 Clojure通常不允许这样做。 (如果你使用Java或Javascript互操作方法,有很多方法可以在不使用Clojure中的原子的情况下更改变量的值。但是,这是你应该只为了互操作或其他一些特殊需要而做的事情。)

另一方面,如果您想限制对Clojure defrecord结构的组件的访问,我认为您不能这样做,除非使用上面给出的方法。您可以使用相关的deftype数据结构来执行此操作,但这通常用于Java互操作。

答案 3 :(得分:0)

请参阅@ JohnPalmer的评论以及Access Control。使用Google可以很容易地找到它。

type  TestPrivate(x)  =
    let y = x - 10
    member private __.X() = 10
    member __.Z() = x + 10
    member __.Y = __.X()

let test = TestPrivate(5)
test.Z() //val it : int = 15
test.Y //val it : int = 10
test.X()
  

错误FS0491:成员或对象构造函数&#39; X&#39;不可访问。   私人会员只能通过声明类型进行访问。   受保护的成员只能从扩展类型访问   不能从内部lambda表达离子访问。

修改

type TestPrivate(x:string)  =
    let totallyPrivate = x 
    member private __.PrivateX = x
    member __.FromOther(other:TestPrivate) = 
        __.PrivateX + "-" + other.Y + "-" + other.PrivateX + "-" + totallyPrivate
    member __.Y = __.PrivateX

let thisTest = TestPrivate("this")
let otherTest = TestPrivate("other")

thisTest.FromOther(otherTest) //val it : string = "this-other-other-this"
otherTest.FromOther(thisTest) //val it : string = "other-this-this-other"
thisTest.FromOther(thisTest) //val it : string = "this-this-this-this"
otherTest.FromOther(otherTest) //val it : string = "other-other-other-other" 

因此,this实例当然可以访问totalPrivate,但thisother都无法访问其他人的绑定,尽管他们可以访问另一个&#39 ; s私有财产。