[翻牆轉]Using Scala Implicits to Replace Mindless Delegation
http://jackcoughonsoftware.blogspot.com/2008/11/using-scala-implicits-to-replace.html
I'm still refactoring my Scala CPU Simulator code, as I keep finding ways to just lop off piles of unneeded code. In this latest example, I was using pretty standard Java style delegation - having my class implement an interface, having a member variable of that interface, and delegating all method calls on that interface to the member variable.
There are several problems with this:
- The main problem: if I add a method to the interface I then have to add it to everyone implementing the interface. But, what If I'm not actually writing the implementation of the interface? Client code won't compile. Adding an implicit doesn't remove this problem entirely, but it certainly works for plain old delegates.
- It's error prone. Maybe the method was a void, and my IDE added the signature for the method, so the code compiles, but I forgot to actually delegate to the member.
- It's just plain wordy and ugly.
To remove the boilerplate, all I needed to do was add an implicit conversion from my class to the interface, using the member variable. I'll show this below.
First, here is an example of the old, more wordy style:
trait PowerSource{
def connect( p: PowerSource ): Unit
def disconnect( p: PowerSource ): Unit
def reconnect( p: PowerSource ): Unit
}
class DelegateToPowerSource( in: PowerSource ) extends PowerSource {
def connect( p: PowerSource ) = in connect p
def disconnect( p: PowerSource ) = in disconnect p
def reconnect( p: PowerSource ) = in reconnect p
}
DelegateToPowerSource
has a member, p
, and implements the PowerSource
interface by delegating to that member for each of the methods on the PowerSource
interface. The more methods that PowerSource
has, the longer DelegateToPowerSource
gets, and yet the code is just boilerplate. Even if the IDE does this for you, its still wordy and potentially error prone.
Now for the new version:
trait PowerSource{
def connect( p: PowerSource ): Unit
def disconnect( p: PowerSource ): Unit
def reconnect( p: PowerSource ): Unit
}
object DelegateToPowerSource{
implicit def delegateToPowerSource( d: DelegateToPowerSource ) = d.p
}
class DelegateToPowerSource( p: PowerSource )
That's it. Now, any time I add a method to the PowerSource
interface (I should probably start calling it trait), DelegateToPowerSource
simply has that method; via the conversion. I never have to change DelegateToPowerSource
because of a change to PowerSource
. DelegateToPowerSource
can simply have whatever code in it that it was originally intended to have, obviously augmenting PowerSource
in some way.
For completeness, I'll post the actual code where I did exactly this. But, it's pretty much the same, so if you get the point, no need to keep reading.
trait LogicGate extends PowerSource{
val inputA: PowerSource
val inputB: PowerSource
val output: PowerSource
}
abstract class BaseLogicGate(val inputA: PowerSource, inputB: PowerSource)
extends LogicGate {
def state = output.state
def -->( p: PowerSource ): PowerSource = output --> p
def <--( p: PowerSource ): PowerSource = output <-- p
def disconnectFrom( p: PowerSource ): PowerSource = output disconnectFrom p
def disconnectedFrom( p: PowerSource ): PowerSource = output disconnectedFrom p
def handleStateChanged( p: PowerSource ) = {}
def notifyConnections = {}
}
class AndGate(a: PowerSource, b: PowerSource)
extends BaseLogicGate(a: PowerSource, b: PowerSource){
val output = new Relay(a, new Relay(b))
}
Of course there were several other LogicGates (or, nor, nand, etc). All of this was condensed down to:
object LogicGate{
implicit def logicGateToPowerSource( lg: LogicGate ): PowerSource = lg.output
}
trait LogicGate{
val inputA: PowerSource
val inputB: PowerSource
val output: PowerSource
}
class AndGate(val inputA: PowerSource, val inputB: PowerSource) extends LogicGate{
val output = new Relay(inputA, new Relay(inputB))
}
BaseLogicGate
is now removed entirely. This is great because BaseLogicGate
was really weird, it didn't even use its inputs. The only reason it was there was to decouple the delegation logic from the actual trait.
Now things have become MUCH more clear. AndGate
is a LogicGate
with inputs A and B, and one output, made from Relay
s.
相關文章
- mac上翻牆Mac
- 春秋雲鏡 Delegation
- Using Script and Style Bundles【翻譯】
- 避免用using包裝DbContext【翻譯】Context
- Scala的ACM轉換ACM
- 翻譯 | Learning React Without Using React Part 2React
- 翻譯 | Learning React Without Using React Part 1React
- xp中的超級命令replace(轉)
- [翻譯]The Neophyte's Guide to Scala Part 12: Type ClassesGUIIDE
- Scala Essentials: 隱式轉換
- Replace
- 使用rownum及replace實現行轉列
- 如何翻牆那點事兒【Android | Windows | macOS】AndroidWindowsMac
- windows下用XX-Net+ipv6+Chrome翻牆WindowsChrome
- [翻譯]使用 ICS KeyChain API(Using the ICS KeyChain API)AIAPI
- 轉享: 在Scala中使用GuiceGUI
- 跨越專業翻譯的語言之牆:百度翻譯的技術攀登
- JavaScript replace()JavaScript
- mysql replaceMySql
- Using a Virtual CListView with a Dynaset (轉)View
- 全面分析防火牆及防火牆的滲透(轉)防火牆
- clone vue-hacknew專案之後 命令列翻牆Vue命令列
- [轉] Scala Try 與錯誤處理
- Scala 將BigDecimal轉換為LongDecimal
- textview - 翻轉動畫TextView動畫
- (字串)句子翻轉字串
- 【Scala】Scala之ObjectObject
- Juniper防火牆簡介(轉)防火牆
- 防火牆介紹(1)(轉)防火牆
- 防火牆介紹(2)(轉)防火牆
- 動態 iptables 防火牆(轉)防火牆
- NAT iptables防火牆(script)(轉)防火牆
- 防火牆埠(下)(轉載)防火牆
- 防火牆埠(中)(轉載)防火牆
- 防火牆埠(上)(轉載)防火牆
- Using WebLogic Server With Oracle RAC(轉)WebServerOracle
- replace函式函式
- [翻譯] USING GIT IN XCODE [6] 在XCODE中使用GIT[6]GitXCode