Single-responsibility principle

單一職責(Single-responsibility principle):一個類(class)應該只因為它負責的那件事而被更改。

注意:在單一職責中,要取得平衡,不要過度的拆分,例如拆分到一個類中只有一個方法。

拆分時可以考慮以下因素:

  • 可讀性(Readability):簡短的類通常比較容易閱讀和理解。雖然“簡短”沒有明確的定義,但通常開發者認為200~300行的類是比較合適的。
  • 擴展性(Extensibility):類是否容易擴展,修改或替換這些類時不應該無意破壞其他部分。
  • 可重用性(Reusability):是否可以更方便地重新使用這些類

以下是一個將聲音,輸入,移動皆包含在一起的Player類,隨著專案的發展,這個類會越來愈難維護,例如更改聲音會動到這個類,更改移動輸入會動到這個類…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class UnrefactoredPlayer : MonoBehaviour
{
[SerializeField] private string inputAxisName;
[SerializeField] private float positionMultiplier;
private float yPosition;
private AudioSource bounceSfx;

private void Start()
{
bounceSfx = GetComponent<AudioSource>();
}

private void Update()
{
float delta = Input.GetAxis(inputAxisName) * Time.deltaTime;
yPosition = Mathf.Clamp(yPosition + delta, -1, 1);
transform.position = new Vector3(transform.position.x, yPosition * positionMultiplier, transform.position.z);
}

private void OnTriggerEnter(Collider other)
{
bounceSfx.Play();
}
}

應考慮將這些行為拆分到各自的類,如 PlayerAudio、PlayerInput、PlayerMovement 等。Player 類仍然依賴這些行為,但這些行為已經被拆分到各自的類中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[RequireComponent(typeof(PlayerAudio), typeof(PlayerInput), typeof(PlayerMovement))]
public class Player : MonoBehaviour
{
private PlayerAudio playerAudio;
private PlayerInput playerInput;
private PlayerMovement playerMovement;

private void Start()
{
playerAudio = GetComponent<PlayerAudio>();
playerInput = GetComponent<PlayerInput>();
playerMovement = GetComponent<PlayerMovement>();
}
}

public class PlayerAudio : MonoBehaviour
{
// PlayerAudio specific implementation
}

public class PlayerInput : MonoBehaviour
{
// PlayerInput specific implementation
}

public class PlayerMovement : MonoBehaviour
{
// PlayerMovement specific implementation
}

評論