This is the second day of my participation in the November Gwen Challenge. Check out the details: the last Gwen Challenge 2021

Introduction: Class A depends directly on class B, and if you want to change class A to depend on class C, you must modify the code of class A to do so. In this scenario, class A is typically A high-level module responsible for complex business logic. Classes B and C are low-level modules responsible for basic atomic operations; If class A were modified, it would introduce unnecessary risk to the program.

Solution: Change class A to depend on abstract I, class B and class C implement abstract I respectively, and class A is indirectly related to either class B or class C through interface I. This will greatly reduce the probability of changing class A, in effect decoupling.

(1) definition:

1. High-level modules should not depend on low-level modules, they all depend on abstractions (interfaces, abstract classes, etc.), abstractions should not depend on details, details should depend on abstractions.

2. Program for interfaces, not implementations.

(2) Implementation analysis:

A) Code should depend on abstract classes, not concrete classes, high-level modules should not depend on low-level modules, and code for interfaces, not implementations or concrete classes.

B) A common way to implement the dependency reversal principle is to use abstract classes or interfaces in code.

C) The dependency inversion principle requires that clients rely on abstract coupling, and coupling in an abstract way is the key to dependency inversion

D) Dependency injection:

Inject instance variables through constructors.

Inject instance variables through Setter methods.

Inject instance variables through interface methods.

(2) sample:

There are a lot of features in games that allow the player to attack various monsters.

 

The original scheme is as follows:




public class Player : MonoBehaviour {

 

void Start () {

 

        Attack(new SoliderMonster());

    }

    void Attack(SoliderMonster soliderMonster){ soliderMonster.TakeAttack(); }}public class SoliderMonster

{

    public void TakeAttack()

    {

        Debug.Log("SoliderMonster has been attacked"); }}Copy the code

Everything seemed to go wrong, but when we changed to attacking other types of monsters, we had to modify the high-level module Attack method to suit our needs, which obviously didn’t meet our requirements. Depending on the solution to the introduced problem, we could have a Monster abstract class and then have various monsters inherit it and implement that abstraction, and then have characters depend on that abstraction. The refactoring is as follows.


using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class Player : MonoBehaviour {

 

 

    private Monster monster;

    void Start () {

        Setter(new MasterMonster());

        Attack();

        Setter(new SoliderMonster());

        Attack();

    }

 

    public void Setter(Monster target)

    {

        monster = target;

    }

    void Attack(){ monster.TakeAttack(); }}public abstract class Monster

{

    public abstract void TakeAttack();

}

 

public class SoliderMonster: Monster

{

    public override void TakeAttack()

    {

        Debug.Log("SoliderMonster has been attacked"); }}public  class MasterMonster: Monster

{

    public override void TakeAttack()

    {

        Debug.Log("MasterMonster attacked"); }}Copy the code

Now when we add new monsters, we just need to make our new concrete monster class inherit from the abstract monster class, and the player class is basically unchanged.

Other dependency injection methods:

Above we used Setter methods to inject instance variables. Constructor injection, interface method injection, of course.

Interface methods inject instance variables:

 void Attack(Monster monster)
    {
        monster.TakeAttack();
    }
Copy the code

Inject instance variables via constructors, it is worth noting that Unity classes that inherit from MonoBehaviour cannot be constructed via constructors using the new keyword.

 public Player(Monster target)
    {
        monster = target;
    }
Copy the code