Swift模块/类如何解决缺乏语言支持的问题?#34; protected"成员?

时间:2018-03-04 15:49:21

标签: swift access-control

我面临的情况是我在模块中定义了一个可重用的基类,我想提供某些应该只能由子类调用的函数,而不是该子类的外部用户。

我正在编写框架并将其打包为Swift模块。我的框架的一部分包括一个可以进行子类化以添加功能的基类,但是派生类也有一个进一步的外部目的。想象一下定义一种新的视图:它来自class JavaWordBlacklist { private static volatile Broadcast<List<String>> instance = null; public static Broadcast<List<String>> getInstance(JavaSparkContext jsc) { if (instance == null) { synchronized (JavaWordBlacklist.class) { if (instance == null) { List<String> wordBlacklist = Arrays.asList("a", "b", "c"); instance = jsc.broadcast(wordBlacklist); } } } return instance; } } class JavaDroppedWordsCounter { private static volatile LongAccumulator instance = null; public static LongAccumulator getInstance(JavaSparkContext jsc) { if (instance == null) { synchronized (JavaDroppedWordsCounter.class) { if (instance == null) { instance = jsc.sc().longAccumulator("WordsInBlacklistCounter"); } } } return instance; } } wordCounts.foreachRDD((rdd, time) -> { // Get or register the blacklist Broadcast Broadcast<List<String>> blacklist = JavaWordBlacklist.getInstance(new JavaSparkContext(rdd.context())); // Get or register the droppedWordsCounter Accumulator LongAccumulator droppedWordsCounter = JavaDroppedWordsCounter.getInstance(new JavaSparkContext(rdd.context())); // Use blacklist to drop words and use droppedWordsCounter to count them String counts = rdd.filter(wordCount -> { if (blacklist.value().contains(wordCount._1())) { droppedWordsCounter.add(wordCount._2()); return false; } else { return true; } }).collect().toString(); String output = "Counts at time " + time + " " + counts; } UIView,然后提供额外的逻辑,然后由另一方实例化。

在这种情况下,我是那个定义NSView类似于要被子类化的类的人,随之而来的还有很多私有UIView内部的东西,比如测量,安排,谁知道,内部的东西。

关键是,这个新视图类的最终用户不希望看到支持子类的体系结构的内部,那些应该完全位于子类所代表的黑盒子内。

令我感到震惊的是,这在Swift中是不可能的。

我真的不明白为什么Swift摆脱了UIView访问控制。 According to Apple,我想要只暴露给子类的函数&#34;在子类之外并不是很有用,所以保护并不重要&#34;。

我错过了什么吗?这是Swift根本无法支持的一整套设计模式吗?

我想到的一个想法是,我可能将我班级的公共 - 公共部分和私人 - 公共部分分成两部分,可能使用协议,公共 - 公共用户只能看到公共协议和&# 34;私人&#34;公共用户会看到&#34;私人&#34;协议也是如此。唉,对于以前过于自由的东西来说,这似乎是很多工程。

2 个答案:

答案 0 :(得分:2)

你可以通过将for-subclasses的东西分离成一个单独的协议来解决这个问题,比如:

          Mir wurde ganz schwer ums Herz.
          Ich hoffte und betete,

然后你至少可以将那些不被调用的东西分组在一个地方,以避免它污染公共接口,而不是绝对必要,并且偶然被叫。

您也可以在此重新考虑是否确实需要子类模式,并考虑使用协议。不幸的是,由于协议不能嵌套类型,这涉及为子类特定协议提供Objective-C风格的前缀名称:

perl -0777 -pe 's/^[1-9]\d{13}\.\d{3}\\|^[1-9]\d{13}\.\d{3}\|[A-Z]{2}[0-9]\|//g'  clean.txt 

答案 1 :(得分:2)

FWIW - 自从Swift中存在访问控制之前,我一直在不断要求在Swift中进行更好的访问控制(包括protected)。现在,在我们被告知将Swift方法用于访问控制尝试的3.5年后,Swift已经成为我近三年的主要语言了,我仍然认为访问控制范例是笨拙的,无法模拟易于理解的概念几乎所有类似的语言。

对我来说,最大的缓解因素是Swift已经让我远离使用继承和95%的时间继承子类,我认为这是一件好事。所以这个问题比其他情况要少。但是对于与您描述的情况完全相同的情况,没有相同的方法可以仅使用协议和协议扩展来完成您正在执行的操作,因此您可能会污染可能有害内部详细信息的公共API,或使用某些解决方法(如接下来的那个公开API暴露最小,并以模板和笨拙的代价模拟你想要的东西。

也就是说,我采用的方法有点受到Objective C的启发,其中也没有真正的protected访问控制,但约定是声明一个公共API头(客户端代码将导入和引用)以及一个特殊的“+ Subclassing”标题,只有子类将在其实现中导入,使他们能够看到非公共消费内部。

在Swift中,这也不是直接可行的,但给出了这样的类:

open class SomeClass {
    private var foo: String
    private var bar: Data
    public init(){
        foo = "foo"
        bar = Data()
    }
    private func doInternalThing() {
        print(foo)
    }
}

您可以通过扩展添加嵌套的“受保护”包装器(必须与您的类声明在同一个文件中),它接受类(或子类)的实例并将受保护级别的内部结构公开为排序代理人:

// Create a nested "Protected" type, which can accept an instance of SomeClass (or one of its subclasses) and expose the internal / protected members on it
public extension SomeClass {
    public class Protected {
        unowned private var someClass: SomeClass
        public var foo: String {
            get {
                return someClass.foo
            }
            set {
                someClass.foo = newValue
            }
        }
        public init(_ someClass: SomeClass) {
            self.someClass = someClass
        }
        public func doInternalThing() {
            someClass.doInternalThing()
        }
    }
}

在框架之外,在客户端应用程序中,受保护的成员在子类中访问,如下所示:

class SomeSubclass: SomeClass {
    private lazy var protected: SomeClass.Protected = { SomeClass.Protected(self) }()
    func doSomething() {
        protected.foo = "newFoo"  // Accesses the protected property foo and sets a new value "newFoo"
        protected.doInternalThing() // Prints "newFoo" by calling the protected method doInternalThing which prints the foo property.
    }
}

这种方法有利有弊。缺点主要是您需要编写的样板数量,以便将所有属性和函数从Protected包装器映射到实际的类实例,如上所示。此外,没有避免消费者将SomeClass.Protected视为公开可见类型的事实,但希望很明显它不应该被使用,并且很难随意使用它不会发生。

优点是,在创建子类时,客户端没有太多的样板或痛苦,并且很容易声明一个懒惰的“受保护”var来获取所需的API。非子类很可能偶然或无意中偶然发现或使用此API,并且它大部分都是隐藏的。 SomeSubclass的实例不会在代码完成或外部代码中显示任何额外受保护的API。

我鼓励其他任何认为访问控制 - 或者实际上是这种情况下,API可见性和组织 - 的人比今天的Swift更容易让Swift团队通过Swift论坛,Twitter或bugs.swift知道。有机