问得好!原则上,您要寻找的可见性修改器称为protected
.
// NOTE: Non-working code; see below for explanation
interface Product {
var count: Int
protected set
fun add(i: Int) {
count += i
}
fun remove(i: Int) {
count -= i
}
}
class Salami(count: Int) : Product {
override var count: Int = count
protected set // Error
}
如果这样做有效,它将显示"count
可以公开读取,但只能从Product
及其子类赋值",这听起来就是您想要的.
遗憾的是,JVM(扩展名为Kotlin)不允许在接口中使用protected
修饰符.出现这种情况的原因在other-places中已经讨论过了,但是不管是什么原因,它现在都不会起作用,这使得您想要编写的设计模式很难实现.
因此,我们只能求助于较旧的Java样式模式.你可以随时将你的界面设置为abstract class
(带有所有随之而来的限制).
abstract class Product(count: Int) {
var count: Int = count
private set
fun add(i: Int) {
count += i
}
fun remove(i: Int) {
count -= i
}
}
class Salami(count: Int) : Product(count) {}
但现在调用者必须扩展类,而不是实现接口,这限制了他们只能进行单继承.
相反,您可以使用抽象实现模式,在接口中有默认实现之前,Swing等标准Java库就使用该模式.使用这种模式,您的interface Product
是完全抽象的,并且没有默认实现.但随后您提供了一个抽象类AbstractProduct
,它整齐地实现了所有方法.如果需要扩展另一个类,调用者仍然可以自由地实现方法themselves,但大多数调用者只会扩展AbstractProduct
.在你的情况下,应该是这样的.
interface Product {
val count: Int
fun add(i: Int): Unit
fun remove(i: Int): Unit
}
abstract class AbstractProduct(count: Int) : Product {
// Note: final modifier is required here since we have a private
// setter, which cannot be open.
final override var count: Int = count
private set
override fun add(i: Int) {
count += i
}
override fun remove(i: Int): Unit {
count -= i
}
}
现在,大多数应用程序都会是这样的.
class Salami(count: Int) : AbstractProduct(count) {}
但是,如果Salami
需要扩展Meat
或其他类,它可以自己这样做.
class Salami(count: Int) : Meat(), Product {
final override var count: Int = count
private set
override fun add(i: Int) {
count += i
}
override fun remove(i: Int): Unit {
count -= i
}
}
因此,如果需要,用户仍然可以获得完整的通用性,但在简单的情况下,一切都会正常工作.它不是完美的,但它接近你想要的.只需记住,产品应该始终使用Product
个函数taking,而不是AbstractProduct
个函数,因为后者是一个实现细节.
Composition over Inheritance
最后,您可以总是偏爱组合而不是继承.你得到的这个count
设置是它自己的封装的子系统,所以我们可以明确地说明这一点.
class ProductCount(initialValue: Int) {
var value: Int = initialValue
private set
fun add(i: Int): Unit {
value += i
}
fun remove(i: Int): Unit {
value -= i
}
}
interface Product {
val productCount: ProductCount
}
class Salami(count: Int) : Product {
override val productCount = ProductCount(count)
}
现在,only,ProductCount
人可以修改计数.除了提供给它的add
和remove
方法之外,甚至连Product
都不能做到这一点.我们已经将产品的数量与产品本身脱钩.而且,如果您愿意,您甚至可以在product
上提供委托方法,在ProductCount
上调用相同的方法,以使API看起来更像您原来的API.
interface Product {
val productCount: ProductCount
fun add(i: Int): Unit = productCount.add(i)
fun remove(i: Int): Unit = productCount.remove(i)
val count: Int
get = productCount.value
}
现在,您的公共API完全符合您的要求.你有一个count
的整数变量,不能从外部修改,你有add
和remove
个方法,do可以修改它.所有这些都被封装在一个ProductCount
类中,这一事实对您的API用户来说是隐藏得相当好的.