C# 设计模式
发布时间 :
字数:4.6k
C#设计模式
什么是设计模式?
- 在长久的面向对象开发过程以来,遇到种种的场景和问题,提出的解决问题的思路和方案。沉淀下来后,总结出来的解决问题的套路
什么是设计原则?
- 面向对象过程中,前辈大咖们推荐的一些指导性原则 遵循这些原则可以让你的设计更有竞争力。
六大设计原则
单一职责原则(SRP:Single Responsibility Principle)
单一职责原则,SRP又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因。该原则由罗伯特C.马丁(Robert C.Martin)于《敏捷软件开发:原则、模式和实践》一书中给出的。
里氏替换原则(LSP:Liskov Substitution Principle)
里氏替换原则,OCP作为OO的高层原则,主张使用“抽象(Abstraction)"和“多态(Polymorphism)"将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。
迪米特法则(LKP:Least Knowledge Principle)
迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话.
定义
- 一个对象应该对其他对象保持最少的了解 只与直接朋友进行通信。
类与类之间的关系:
纵向:继承关系
横向:聚合、组合、关联、依赖「出现在方法内部」
高内聚、低耦合
降低耦合度的方法
- 少使用类的继承,多用接口隐藏实现的细节。
- 模块的功能化分尽可能的单一,道理也很简单,功能单一的模块供其它模块调用的机会就少。(其实这是高内聚的一种说法,高内聚低耦合一般同时出现)。
- 遵循一个定义只在一个地方出现
- 少使用全局变量。
- 类属性和方法的声明少用public,多用private关键字。
- 多用设计模式,比如采用MVC的设计模式就可以降低界面与业务逻辑的耦合度。
- 尽量不用”硬编码”的方式写程序,同时也尽量避免直接用SQL语句操作数据库.
- 避免直接操作或调用其它模块或类(内容耦合);如果模块间必须存在耦合,原则上尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,避免使用内容耦合。
增强内聚度方法
- 模块只对外暴露最小限度的接口,形成最低的依赖关系。
- 只要对外接口不变,模块内部的修改,就不得影响其他模块;
- 删除一个模块,应当只影响有依赖关系的其他模块,而不应该影响其他无关部分;
- 通过降低访问修饰符权限,减少联系,减少耦合
依赖倒置原则(DIP:Dependence Inversion Principle)
依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
- 定义
- 高层模块不应该依赖于低层模块,两者应该依赖抽象 而不是依赖细节。
- 高层:方法调用方
- 底层:被调用方
- 面向抽象编程
- 属性、字段、方法参数、返回值,一切都尽量使用抽象【类接口】
- 抽象不变,高层就不变
- 抽象一般是稳定的,低层的扩展变化不会影响到高层,低层就可以横向的自由扩展,架构稳定
- 80%的设计模式跟抽象有关
- 抽象的好处
- 一个方法可以满足不同类型的参数传入
- 支持动态扩展,只要是实现了这个抽象,不需要修改上层
- 实例:学生类实现不同手机的使用方法,学生依赖手机,新手机出现时,学生类也要更新新方法。
- 不同的手机应该依赖抽象(手机),学生类也应该依赖于抽象(手机用户)
接口隔离原则(ISP:Interface Segregation Principle)
客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。
- 定义:
- 使用多个专门的接口比使用单一的总接口要好,但也不建议一个接口只对应一个方法。
- 一个类对另外一个类的依赖性应当是建立在最小的接口上的。
- 一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。
- 接口的正确定义
- 既不能大而全,也不能一个接口一个方法
- 应该按照功能的密不可分来定义接口
- 应该是动态的,随业务变化而变化,设计的时候要留好提前量,避免抽象的变化
- 实例:手机的核心功能就是打电话和发短信,拍照、上网等其他功能的接口,不要被手机所依颗。
- 参看.Net类中的接口的设计与实现
开闭原则(OCP:Open Closed Principle)【总则】
在面向对象编程领域中,开闭原则规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。该特性在产品化的环境中是特别有价值的,在这种环境中,改变源代码需要代码审查,单元测试以及诸如此类的用以确保产品使用质量的过程。遵循这种原则的代码在扩展时并不发生改变,因此无需上述的过程。
- 定义
- 开闭原则是一个目标,没有任何手段,又被称为总则
- 为什么要遵循开闭原则
- 面向对象语言是静态语言,最害怕变化,因为会波及很多东西。
- 最理想的就是新增类,对原代码没有改动,原有代码才是可信的
- 遇到需求变更该怎么办呢?
- 直接修改现有方法 (最不可取)
- 增加方法 (稍好一些)
- 增加类 (那更好啦)
- 增加类库/框架 (那最好啦)
设计模式
简单工厂模式
Csharp
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| public class LegencyFunction : MonoBehaviour {
private void Start() { #region Old
float? result = Count(10, 21, "*");
Debug.Log(result);
#endregion
#region New
BinaryOperation operation = OperationFactory.CreateOperation(1, 2, "+"); operation.SetNumber(2,3); float newresult = operation.GetResult(); Debug.Log(newresult);
#endregion }
#region Old
public float? Count(float num01, float num02, string sign) { switch (sign) { case "+": return num01 + num02; case "-": return num01 - num02; case "*": return num01 * num02; case "/": return num01 / num02; case "%": return num01 % num02; default: return null; }
}
#endregion
public abstract class BinaryOperation { protected float number01; protected float number02;
public BinaryOperation() { }
public void SetNumber(float number01,float number02) { this.number01 = number01; this.number01 = number02; }
public BinaryOperation(float number01, float number02) { this.number01 = number01; this.number02 = number02; }
public abstract float GetResult(); }
public class PlusOperation : BinaryOperation { public PlusOperation(float number01, float number02) : base(number01, number02) { }
public override float GetResult() { return number01 + number02; } }
public class SubtractionOperation : BinaryOperation { public SubtractionOperation(float number01, float number02) : base(number01, number02) { }
public override float GetResult() { return number01 - number02; } }
public class MulOperation : BinaryOperation { public MulOperation(float number01, float number02) : base(number01, number02) { }
public override float GetResult() { return number01 * number02; } }
public class OperationFactory {
public static BinaryOperation CreateOperation(float number01, float number02,string sign) { switch (sign) { case "+": return new PlusOperation(number01, number02); case "-": return new SubtractionOperation(number01, number02); case "*": return new MulOperation(number01, number02); default: return null; } } } }
|
Unity
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| public class UnitAssetsFactory {
public static UnityAsset CreateUnityAsset(string assetType) { switch (assetType) { case "GameObject": return new PrefabAsset(""); case "Sprite": return new SpriteAsset(""); default: return null; } } }
public abstract class UnityAsset {
public string path; protected object asset;
public UnityAsset(string path) { this.path = path; }
public void Load() { asset = Resources.Load(path); } public abstract void Instantiate(); public abstract void Show();
}
public class PrefabAsset : UnityAsset { public PrefabAsset(string path) : base(path) { }
public override void Instantiate() { GameObject go = GameObject.Instantiate(asset as GameObject); }
public override void Show() { } }
public class SpriteAsset : UnityAsset { public SpriteAsset(string path) : base(path) { }
public override void Instantiate() { GameObject go = new GameObject("Sprite"); SpriteRenderer spriteRenderer = go.AddComponent<SpriteRenderer>();
Sprite sprite = Sprite.Create(asset as Texture2D, new Rect(0, 0, 100, 100), Vector2.zero); sprite.name = "sprite"; spriteRenderer.sprite = sprite; }
public override void Show() { } }
public class UnitySingtonFactory : MonoBehaviour {
public static UnitySingtonFactory instance;
private void Awake() { instance = this; DontDestroyOnLoad(gameObject); }
private void OnEnable() { }
public T GetAsset<T>(string assetPath) where T : Object { return Resources.Load<T>(assetPath); }
public T[] GetAssets<T>(string assetsPath) where T : Object { return Resources.LoadAll<T>(assetsPath); } }
public class UnityFactoryConsole : MonoBehaviour {
private void Start() {
GameObject cubePrefab = UnitySingtonFactory.instance.GetAsset<GameObject>("Cube"); Instantiate(cubePrefab);
} }
|
策略模式
工厂模式输入参数返回对象obj
策略模式输入参数返回方法
Csharp
Unity 游戏角色攻击方式不同
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
| public abstract class Skill {
public GameHero[] AttackTargets { get; set; }
public GameHero SkillFireHero { get; set; }
public float[] SkillParameters { get; set; }
public float Damage { get; set; }
public abstract void TakeDamage();
}
public class CircleSkill : Skill { public override void TakeDamage() { Vector3 playerPos = SkillFireHero.HeroTra.position; Vector3 skillCenter = playerPos + SkillFireHero.HeroTra.forward * SkillParameters[0];
for (int i = 0; i < AttackTargets.Length; i++) { float dis = Vector3.Distance(skillCenter, AttackTargets[i].HeroTra.position);
if (dis <= SkillParameters[1]) { AttackTargets[i].TakeDamage(Damage); } } } }
public class CubeSkill : Skill { public override void TakeDamage() { Vector3 playerPos = SkillFireHero.HeroTra.position; Vector3 skillCenter = playerPos + SkillFireHero.HeroTra.forward * SkillParameters[0];
for (int i = 0; i < AttackTargets.Length; i++) { Vector3 dir = AttackTargets[i].HeroTra.position - skillCenter;
Vector3 rightDir = Vector3.Project(dir, SkillFireHero.HeroTra.right); Vector3 forwardDir = Vector3.Project(dir, SkillFireHero.HeroTra.forward); if (rightDir.magnitude < SkillParameters[1]/2 && forwardDir.magnitude < SkillParameters[2]/2) { AttackTargets[i].TakeDamage(Damage); } } } }
public class SectorSkill : Skill { public override void TakeDamage() { } }
public class GameHero {
private string name; public float HeroHP { get; set; } public Transform HeroTra { get; set; } public Skill Skill { get; set; }
public GameHero(string name) { this.name = name; }
public void FireSkill() { Skill.SkillFireHero = this; Skill.TakeDamage(); }
public void TakeDamage(float damage) { HeroHP -= damage;
Debug.Log(name + "收到伤害" + damage); } }
public class StrategyConsole : MonoBehaviour {
public Transform angelTra; public Transform libaiTra;
private IEnumerator Start() { GameHero angel = new GameHero("安琪拉"); angel.HeroHP = 1000; angel.HeroTra = angelTra; GameHero libai = new GameHero("李白"); libai.HeroHP = 1200; libai.HeroTra = libaiTra;
GameHero[] allHeros = new GameHero[] { angel, libai };
Skill skill01 = new CubeSkill(); skill01.Damage = 100; skill01.SkillParameters = new float[] { 5, 4, 3 }; skill01.AttackTargets = allHeros;
Skill skill02 = new CircleSkill(); skill02.Damage = 100; skill02.SkillParameters = new float[] { 5, 3 }; skill02.AttackTargets = allHeros;
angel.Skill = skill01; libai.Skill = skill02;
while (true) { yield return 0; angel.FireSkill(); } } }
|
外观模式
Csharp
Unity 框架启动模式
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| public class UIFrameFacade {
public void FrameStart() { UIAssets.instance.LoadPanel("MainPanel"); UIManager.instance.PushPanel("MainPanel"); } }
public class UIAssets {
public static readonly UIAssets instance = new UIAssets(); private UIAssets() { }
public void LoadPanel(string panelName) { Debug.Log(panelName + "的资源已经加载完成!"); } }
public class UIManager {
public static readonly UIManager instance = new UIManager(); private UIManager() { }
public void PushPanel(string panelName) {
Debug.Log("已经打开窗口..." + panelName);
} }
public class FacadeConsole : MonoBehaviour {
private void Start() { UIFrameFacade facade = new UIFrameFacade(); facade.FrameStart();
} }
public class DemoWInodwFacade : MonoBehaviour {
private Button[] buttons;
private void Awake() { buttons = GetComponentsInChildren<Button>(); }
public void BindEvent(UnityAction unityAction) { for (int i = 0; i < buttons.Length; i++) { buttons[i].onClick.AddListener(unityAction); } } }
|
观察者模式
Unity
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
| public class NormalObserver : MonoBehaviour {
public float interval = 3f; private float timer = 0;
private void Update() { timer += Time.deltaTime;
if (timer > interval) {
timer = 0; } } }
public abstract class AbObserver {
public abstract void ReveiveBeatifulGirlMsg(string canDateTime);
}
public class Observer : AbObserver { public string name;
public Observer(string name) { this.name = name; }
public override void ReveiveBeatifulGirlMsg(string canDateTime) { Debug.Log(name + canDateTime); } }
public interface AbSubject {
void AddObserver(AbObserver observer); void RemoveObserver(AbObserver observer);
void Notify();
}
public class Subject : MonoBehaviour, AbSubject { private IList<AbObserver> observers;
private string canDateTime = null;
private void Awake() { observers = new List<AbObserver>(); }
private void Update() { if (BeautifulGirl.instance.CanData()) { canDateTime = Time.time.ToString(); Notify(); } }
public void AddObserver(AbObserver observer) { if (!observers.Contains(observer)) observers.Add(observer); }
public void RemoveObserver(AbObserver observer) { if (observers.Contains(observer)) observers.Remove(observer); }
public void Notify() { for (int i = 0; i < observers.Count; i++) { observers[i].ReveiveBeatifulGirlMsg(canDateTime); } } }
public class BeautifulGirl : MonoBehaviour {
private int canDateProbability = 1;
public static BeautifulGirl instance;
private void Awake() { instance = this; }
private void OnEnable() { }
public bool CanData() { return Random.Range(1, 101) <= canDateProbability; } }
public class ObserverConsole : MonoBehaviour {
private void Start() {
AbObserver ceo = new Observer("老王"); AbObserver cto = new Observer("老李"); AbObserver cfo = new Observer("老刘");
AbSubject subjectZhao = GetComponent<Subject>();
subjectZhao.AddObserver(ceo); subjectZhao.AddObserver(cto); subjectZhao.AddObserver(cfo); } }
|
代理模式
Csharp
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| public class CsharpProxy : MonoBehaviour {
private void Start() { PayFine self = new NormalDriver("老王","11011111111111111","1010101001"); FriendDrive friendProxy = new FriendDrive("小王", "11016666661111", "666666");
friendProxy.realDriver = self; friendProxy.Pay(200,3); } }
public abstract class PayFine {
public string name; public string violatorID; public string violatorDriveID;
protected PayFine(string name, string violatorID, string violatorDriveID) { this.name = name; this.violatorID = violatorID; this.violatorDriveID = violatorDriveID; }
public abstract void Pay(float money,float score); }
public class NormalDriver : PayFine { public NormalDriver(string name, string violatorID, string violatorDriveID) : base(name, violatorID, violatorDriveID) { }
public override void Pay(float money, float score) { Debug.Log(string .Format( "我是{0},身份证号{1},驾驶证号{2},今交罚款{3},扣除分数{4}", name,violatorID,violatorDriveID,money,score)); } }
public class FriendDrive : PayFine { public PayFine realDriver;
public FriendDrive(string name, string violatorID, string violatorDriveID) : base(name, violatorID, violatorDriveID) { }
public override void Pay(float money, float score) { if (realDriver != null) { Debug.Log("代理人:" + name); realDriver.Pay(money,score); } } }
|
Unity
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| public class UnityProxy : MonoBehaviour {
private void Start() { gameObject.AddComponent<DelayProxy>(); }
private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { MethodInfo methodInfo = GetType().GetMethod("ShowCurrentTime"); DelayProxy.instance.DelayCallByReflection(methodInfo, this, new object[] { Time.time }, 5); } }
public void ShowTime() { Debug.Log("Time:" + Time.time); }
public void ShowCurrentTime(float crtTime) { Debug.Log("移动延时时间:" + crtTime); Debug.Log("执行时间:" + Time.time); } }
public class DelayProxy : MonoBehaviour {
public static DelayProxy instance;
private void Awake() { instance = this; }
public void DelayCallByReflection(MethodInfo methodInfo,object obj,object[] parameters,float delayTime = 3f) { StartCoroutine(IEDelayCallByReflection(methodInfo, obj, parameters, delayTime));
}
IEnumerator IEDelayCallByReflection(MethodInfo methodInfo, object obj, object[] parameters, float delayTime) { yield return new WaitForSeconds(delayTime); methodInfo.Invoke(obj, parameters); }
public void DelayCall(System.Action action,float delayTime = 3f) {
StartCoroutine(IEDelayCall(action, delayTime)); }
IEnumerator IEDelayCall(System.Action action, float delayTime) { yield return new WaitForSeconds(delayTime); action(); } }
|