Johnny850807 / Coding-GYM

自己菜鳥階段時在練習Java物件導向、設計模式時,所設計的題庫,此專案庫目前用來讓我的同學/學生 (欠調教的) 們提交程式碼來進行Review,欲參考OOP題目者可直接到Projects區觀看,題目皆(85%)擁有示範解答程式碼。ISSUE放的都是Code Review嘴砲。(物件導向設計學分兩階段→JAVA→設計模式)
24 stars 6 forks source link

演算法:拓樸排序──依賴注入 #109

Open Johnny850807 opened 5 years ago

Johnny850807 commented 5 years ago

訓練目的:拓樸排序、圖論、依賴注入、Reflection 技術

請先下點功夫,準備好所有材料,依序創建以下類別及程式

  1. 創造抽象Hero, Weapon, Armor, Bullet, Material 介面

    public interface Hero {
    String getInfo();
    }
    public interface Weapon {
    int getPower();
    }
    public interface Armor {
    int getDefence();
    }
    public interface Bullet {
    public int getDamage();
    }
    public interface Material {
    float getExtraPower();
    }
  2. 再創建其子類別

public class MyHero implements Hero{
    private Weapon weapon;
    private Armor armor;

    public MyHero(Weapon weapon, Armor armor) {
        this.weapon = weapon;
        this.armor = armor;
    }

    @Override
    public String getInfo() {
        return String.format("Name: MyHero, Weapon atk: %d, "
                + "Armor dft: %d.", weapon.getPower(), armor.getDefence());
    }

}
public class AK47 implements Weapon{
    private Bullet bullet;
    private Material material;

    public AK47(Bullet bullet, Material material) {
        this.bullet = bullet;
        this.material = material;
    }

    @Override
    public int getPower() {
        return (int) (100 * bullet.getDamage() * material.getExtraPower());
    }
}
public class AK47Bullet implements Bullet{
    @Override
    public int getDamage() {
        return 37;
    }

}
public class Crystal implements Material{

    @Override
    public float getExtraPower() {
        return 1.7f;
    }

}
public class GodShield implements Armor{

    @Override
    public int getDefence() {
        return 800;
    }

}

可以看出,每個建構子中的參數都是依賴於抽象介面型態,才能進行依賴注入 依賴鏈如下圖 image

要產生一個MyHero實例,就必須先創建出Weapon和Armor的實例, 而我們就必須定義各個抽象參數的實例為何種型態,進行依賴注入。 例如:欲注入的Weapon, Armor型態為 AK47, GodShield 而AK47的建構子又有Bullet和Material,須再先創造此兩實例,以此類推。

而因此我們需要一個容器,來供使用者註冊他的實例型態, 未來需要更換實例型態時,只需要改變此容器即可。

  1. 首先創建框架類別 IocContainer及main
public class IocContainer {

    static{
        registerInstance(Hero.class);
        registerInstance(AK47.class);
        registerInstance(AK47Bullet.class);
        registerInstance(Crystal.class);
        registerInstance(GodShield.class);
    }

    public static void registerInstance(Class<?> clazz){
        /**
         * Register all the classes to your container
         */
    }

    public static <T> T inject(Class<?> clazz){
        /**
         * Return the instance of the demanded class object,
         * throw an exception if any classes on the dependency chain is not registered.
         */
        return null;
    }

    public static void main(String[] argv){
        Hero hero = IocContainer.inject(Hero.class);
        System.out.println(hero.getInfo());
    }
}

可以看見我們註冊了,MyHero, AK47, AK47Bullet, Crystal, GodShield, 這代表未來若有建構子需要進行依賴注入,則注入了實例型態會參考於此。 Main函數中只有兩行, 第一行呼叫IOC框架,並令他注入Hero實例到hero物件中, IOC框架會到註冊表中尋找一個已被註冊的Hero的子類別, 因此最後回傳的實例會是MyHero型態。

當然,為了創建MyHero,IOC必須先依賴注入Weapon及Armor的實例,因此需要演算法。

其餘無指出的細節自由設計,如:例外處理。 最後 System.out.println(hero.getInfo()); 應會印出

Name: MyHero, Weapon atk: 62900, Armor dft: 800.

要練習(濫用)拓樸排序的話看這  

image

由於,這種依賴鏈,不可能出現有向環狀的情況(如圖), 因此可以套用拓樸排序演算法:

在註冊完成後,建構出一個有向圖, image 使用拓樸排序將依賴鏈(全序集)創建出來 AK47Bullet -> Crystal -> AK47 -> GodShield -> MyHero

這個全序依賴鏈建立出來之後,就能夠很簡單的利用以下步驟進行依賴注入:

  1. 欲注入Hero實例
  2. 查詢實例 Hero -> MyHero
  3. 查詢依賴鏈 AK47Bullet -> Crystal -> AK47 -> GodShield -> MyHero
  4. 創建建構子中無參數的類別 AK47Bullet, Crystal, GodShield
  5. 創建並注入AK47
  6. 創建並注入MyHero