iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
Software Development

Kotlin on the way系列 第 14

Day 14 OO 能吃嗎? 多型 polymorphism

  • 分享至 

  • xImage
  •  

要了解多型,先來看抽象、封裝、繼承吧

structure

  • 範例
    • 用抽象概念抽取公開介面
  • 多型的威力
  • summary

在白話說一點,用一致的介面做不同的功能,舉個例子
燃油車和電動車,他們引擎的運作方式不同,在駕駛操作方向盤時概念還是一樣的,並不需要去學電動車的操作方式,甚至是每款汽車型號,其內部架構也會有些許出入,但我們只會學一次方向盤、燈號、雨刷外部介面的操作方式,就可以套用在所有通用車上

而其目的在於消除類別之間的耦合,使其更容易維護

一個多型類別依賴圖的範例

範例

我們假設你有一段程式要執行駕駛 Mini 的行為,所以你寫了這個類別

class Mini{
    fun drive(){
        startEngine()
    }
    private fun startEngine() {
        println("mini")
    }
}

fun drive(transportation:Mini){
    transportation.drive()
}

你說這不好嗎?他能執行,能跑的代碼就是好代碼
其實真沒有很好,一個依賴 mini 的韓式,就只有 mini 能使用,今天新需求來了,功能還要可以駕駛 LandRover ,難道要有 driveMinidriveLandRover兩種函式嗎?

用抽象概念抽取公開介面

既然 LandRover 和 Mini 都有共同的抽象概念,我們來為此抽取出一些東西

interface Car {
    fun drive()
    fun startEngine
}
class Mini:Car {
    override fun drive(){
        
    }
    override fun startEngine(){
        
    }
}
class LandRover:Car {
    override fun drive(){
        
    }
    override fun startEngine(){
        
    }
}

fun drive(car:Car) {
    car.drive()
}

到目前為止,已經有不錯的抽象了,我們不管哪種車型,都可以透過 car 的接口操作,但我們仍面對兩個難題

  1. 公開了引擎啟動和駕駛,就有執行順序的問題
  2. 現在依賴在汽車抽象上,但駕駛行為的抽象可以更廣泛

多型的威力

我們最終的程式碼會長這樣,透過DriverInterface 抽象定義了駕駛行為的接口,在abstract class Car裡面,封裝了startEngine 方法,並將其在各類別汽車中實現,另一方面,Carriage也以相同方式封裝,而把 getHorse 隱藏,而對外的公開以 DriveInterface 為主,所有實現了該介面接口的類型,都可以被調用,而不必在意內部實作


fun main() {
    drive(Mini())
    drive(WeddingCarriage())
}

fun drive(transportation:DriverInterface){
    transportation.drive()
}


abstract class Carriage:DriverInterface {
    override fun drive() {
        getHorse()
    }
    protected abstract fun getHorse()
}
class WeddingCarriage:Carriage() {
    override fun getHorse() {
        println("happy")
    }
}


abstract class Car:DriverInterface{
    override fun drive() {
        startEngine()
    }
    protected abstract fun startEngine()
}

class LandRover:Car() {
    override fun startEngine() {
        TODO("Not yet implemented")
    }
}

class Mini:Car(){
    override fun startEngine() {
        println("mini")
    }
}

interface DriverInterface {
    fun drive()
}

summary

多型搭配繼承、抽象、資訊隱藏,可以讓我們在很低的依賴關係下為個類別完成不同的邏輯,運用得好,程式能有良好的內聚、低耦合、好維護等特徵

English

要了解多型,先來看抽象、封裝、繼承吧
In order to understand polymorphism, check out abstract, encapsulation and inheritance

structure

  • Demo
    • substract abstract concept as public interface
  • power of polymorphism
  • summary

To describe polymorphism in one sentence is using same interface to wrap detail, for example, fuel car and electric car, the engine works differently, but the operation on steering wheel is same, you don't have to learn how to drive electric car, even every car might have different architecture, but the user interface is fit is common.

The polymorphism can decouple classes, make the system maintainable

Demo class dependency graph on polymorphism

Demo

Since you want to execute the behavior to drive Mini, you here comes the class

class Mini{
    fun drive(){
        startEngine()
    }
    private fun startEngine() {
        println("mini")
    }
}

fun drive(transportation:Mini){
    transportation.drive()
}

Does it good design?A running program is good program
Actually not, the function relay on mini, therefore only mini can use it, what if you need the function works on LandRover, don't write duplicate code for driveMini and driveLandRover

substract abstract concept as public interface

Since LandRover and Mini has same abstract concept, let's subtract something

interface Car {
    fun drive()
    fun startEngine
}
class Mini:Car {
    override fun drive(){
        
    }
    override fun startEngine(){
        
    }
}
class LandRover:Car {
    override fun drive(){
        
    }
    override fun startEngine(){
        
    }
}

fun drive(car:Car) {
    car.drive()
}

So far, we have a good abstract, whatever model of car can fit the interface of car, but we still facing two issue

  1. by making startengine and drive, there is issue on operator order
  2. Relay on car abstract, but drive behavior include more vehicle

The power of polymorphism

In the end our code will look like this, by usingDriverInterface define interface of drive behavior, inside of abstract class Car encapsulate startEngine method, and implement in each instance of car, on the other side, Carriage encapsulate in same way, hiding getHorse, we exposed DriveInterface, so all the function relay on DriveInterface can handle all kind of vehicle


fun main() {
    drive(Mini())
    drive(WeddingCarriage())
}

fun drive(transportation:DriverInterface){
    transportation.drive()
}


abstract class Carriage:DriverInterface {
    override fun drive() {
        getHorse()
    }
    protected abstract fun getHorse()
}
class WeddingCarriage:Carriage() {
    override fun getHorse() {
        println("happy")
    }
}


abstract class Car:DriverInterface{
    override fun drive() {
        startEngine()
    }
    protected abstract fun startEngine()
}

class LandRover:Car() {
    override fun startEngine() {
        TODO("Not yet implemented")
    }
}

class Mini:Car(){
    override fun startEngine() {
        println("mini")
    }
}

interface DriverInterface {
    fun drive()
}

summary

polymorphism works with inheritance, abstract, hiding information, so we can write decoupled design for different logic


上一篇
Day 13 OO 能吃嗎? 封裝
下一篇
Day 15 Solid 能吃嗎? 單一職責的誤區 Single Responsibility principle
系列文
Kotlin on the way31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言