/**依赖注入用来解耦类之间的引用关系（如A中引用B），被引用类(B)的变化尽量不影响引用类(A)的代码。主要是下列几种方法：
 * 1）构造器注入：在A的构造器中传入B的实例
 * 2）setter注入：在A的实例生成后，为其直接set B的实例
 * 3）工厂方法注入：在A的实例生成后，为其用工厂产生B的实例并传入
 * 4）接口注入：B抽象为接口，B的具体类以继承该接口来生成。A也抽象出一个接口，并在该接口方法中接收B的实例
 */
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance

interface Printer {
    fun init()
    fun print(txt: String?)
}
class ColorPrinter : Printer {
    override fun init() {
        println("启动彩色打印机！")
    }

    override fun print(txt: String?) {
        println("打印彩色文字：$txt")
    }
}
class GreyPrinter : Printer {
    override fun init() {
        println("启动黑白打印机")
    }

    override fun print(txt: String?) {
        println("打印黑白文字：$txt")
    }
}

//简单工厂
object MyPrinterFactory {
    fun createPrinter(name: String):Printer? =
        when(name) {
            "color" -> ColorPrinter()
            "grey" ->  GreyPrinter()
            else->null
        }
}

//工厂方法，反射的使用减少了具体工厂类的数量
abstract class PrinterCreation{
    abstract fun <T:Printer> createPrinter(p: KClass<T>):T
}
class PrinterFatcory:PrinterCreation(){
    override fun <T:Printer> createPrinter(p:KClass<T>):T {
       val  printer:T =p.createInstance()
         return printer
    }
}

class Computer{
    var p: Printer=ColorPrinter()
}

class Computer1(var p: Printer)

class Computer2 {
    var p: Printer? = null
}

interface Computeable {
    fun inject(p: Printer?)
}
class Computer4 : Computeable {
    var p: Printer? = null
    override fun inject(p: Printer?) {
        this.p = p
    }
}

fun main() {
    //无依赖注入
    val computer = Computer()
    computer.p.print("打印测试页...无依赖注入")

    val printer:Printer = ColorPrinter()
    /*等效替代
     val printer:Printer = GreyPrinter()*/

    //构造器注入
    val computer1 = Computer1(printer)
    computer1.p.print("打印测试页...构造器注入")

    //setter注入
    val computer2 = Computer2()
    computer2.p = printer
    computer2.p?.print("打印测试页...setter注入")

    //工厂方法注入
    val computer3 = Computer2()
    computer3.p = MyPrinterFactory.createPrinter("grey")
    /*等效替代
    computer3.p=PrinterFatcory().createPrinter(GreyPrinter::class)*/
    computer3.p?.print("打印测试页...工厂方法注入")


    //接口注入
    val computer4 = Computer4()
    computer4.inject(printer)
    //实现不用new关键字创建打印机实例
    computer4.p?.print("打印测试页...接口注入")
}

