Skip to main content

Listkov Substitution Principle

Ta zasada mówi, że w miejscu klasy bazowej można użyć dowolnej klasy pochodnej (zgodność wszystkich metod). Zasada ta jest niejako rozszerzeniem zasady Open/Close Principle pod względem zachowania.

Przykład złamania tej zasady:

public interface Character {
        void go();
        void run();
        void fight();
}
public class Battlemage implements Character {
    @Override
    public void go() {
        System.out.println("Idę w swoim lekkim pancerzu!");
    }
    @Override
    public void run() {
        System.out.println("Biegnę w swoim lekkim pancerzu!");
    }
    @Override
    public void fight() {
        System.out.println("Wyjmuję swój kostur i rzucam czary!");
    }
}
public class Healer implements Character {
    @Override
    public void go() {
        System.out.println("Idę w swojej szacie!");
    }
    @Override
    public void run() {
        System.out.println("Biegnę w swojej szacie!");
    }
    @Override
    public void fight() {
        throw new IllegalArgumentException();
    }
}
public class Knight implements Character {
    @Override
    public void go() {
        System.out.println("Idę w swoim ciężkim pancerzu!");
    }
    @Override
    public void run() {
        System.out.println("Biegnę w swoim ciężkim pancerzu!");
    }
    @Override
    public void fight() {
        System.out.println("Wyjmuje swój miecz i walczę!");
    }
}
public class Thief implements Character {
    @Override
    public void go() {
        System.out.println("Idę w swoim średnim pancerzu!");
    }
    @Override
    public void run() {
        System.out.println("Biegnę w swoim średnim pancerzu!");
    }
    @Override
    public void fight() {
        System.out.println("Rzucam swoimi zatrutymi sztyletami!");
    }
}
public class ShowBattleInfo {
    public void showInfo(Character[] characters){
        for (Character character : characters) {
            character.go();
            character.run();
            character.fight();
        }
    }
}

Mamy tu interface Character z trzema metodami go, run oraz fight, a także klasy które po niej dziedziczą. Wszystkie klasy oprócz klasy Healer walczą, bo zakładam że heal faktycznie będzie tylko leczył. Skoro jednak klasa Healer implementuje interface Character to w metodzie fight musimy rzucić odpowiednim wyjątkiem – co łamie zasadę.

Przykład zastosowania zasady:

W zasadzie jedną ze zmian jest rozłączenie interface Charakter na dwa: FightCharacter oraz MovingCharacter. Drugą zmianą jest implementacja odpowiednich interfejsów do odpowiednich klas. Zatem nasza klasa Healer będzie implementowała tylko interface MovingCharacter a reszta klas oba. Mamy również klasy ShowBattleInfo i ShowMoveInfo, które posłużą nam do wyświetlenia informacji. Pokaże kod interfejsów oraz klasy BattleMage oraz Heal

public interface FightCharacter {
    void fight();
}
public interface MovingCharacter {
    void go();
    void run();
}
public class Battlemage implements FightCharacter, MovingCharacter{
    @Override
    public void fight() {
        System.out.println("Wyjmuję swój kostur i rzucam czary!");
    }
    @Override
    public void go() {
        System.out.println("Idę w swoim lekkim pancerzu!");
    }
    @Override
    public void run() {
        System.out.println("Biegnę w swoim lekkim pancerzu!");
    }
}
public class Healer implements MovingCharacter{
    @Override
    public void go() {
        System.out.println("Idę w swojej szacie!");
    }
    @Override
    public void run() {
        System.out.println("Biegnę w swojej szacie!");
    }
}

Zobaczmy jak wygląda main:

public class App {
    public static void main(String[] args){

        Knight knight = new Knight();
        Thief thief = new Thief();
        Battlemage battlemage = new Battlemage();
        Healer healer = new Healer();

        ShowMoveInfo showMoveInfo = new ShowMoveInfo();
        ShowBattleInfo showBattleInfo = new ShowBattleInfo();

        FightCharacter[] fightCharacter = {knight,thief,battlemage};
        MovingCharacter[] movingCharacter = {knight,thief,battlemage,healer};

        showMoveInfo.getMoveInfo(movingCharacter);
        System.out.println();
        showBattleInfo.getBattleInfo(fightCharacter);

    }
}

 

pliki do pobrania:
SOLID Download