Notifications
Article
设计模式(一)
Updated 19 days ago
11
0
Day 1
Jack做了一套模拟鸭子的游戏《SimDuck》,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱的叫。她设计了一个鸭子的父类,并让各种鸭子继承自此类,代码结构如下。
/// <summary> /// 父类鸭子 /// </summary> public class Duck { public virtual void quack() { //呱呱叫 } public virtual void swim() { //游泳 } public virtual void display() { //外观 } }
using UnityEngine; /// <summary> /// 野鸭 /// </summary> public class MallardDuck : Duck { public override void display() { //外观是野鸭 Debug.Log("外观是野鸭"); } }
using UnityEngine; /// <summary> /// 红头鸭子 /// </summary> public class RedHeadDuck : Duck { public override void display() { //外观是红头鸭 Debug.Log("外观是红头鸭"); } }
这时新需求来了,要让鸭子会飞!
那还不简单,直接在父类的鸭子里增加一个Fly的方法,不就行了,你的代码如下。
//只需要改动鸭子的父类即可 /// <summary> /// 父类鸭子 /// </summary> public class Duck { public virtual void quack() { //呱呱叫 } public virtual void swim() { //游泳 } public virtual void display() { //外观 } public virtual void fly() { //飞 } }
这时新需求又来了,增加橡胶鸭和木头鸭。
这还不简单,你直接一套继承完事。这时候你的代码如下。
using UnityEngine; /// <summary> /// 橡胶鸭 /// </summary> public class RubberDuck : Duck { public override void quack() { //覆盖成吱吱叫 //橡胶鸭子吱吱叫,没玩过吗? Debug.Log("覆盖成吱吱叫"); } public override void display() { //外观是橡胶鸭 Debug.Log("外观是橡胶鸭"); } public override void fly() { //橡胶鸭子不会飞,什么都不做 } }
using UnityEngine; /// <summary> /// 木头鸭子 /// </summary> public class DecoyDuck : Duck { public override void quack() { //木头鸭子不会叫,什么这里什么都不做 } public override void display() { //外观是木头鸭 Debug.Log("外观是木头鸭"); } public override void fly() { //木头鸭子也不会飞,什么都不做 } }
这时候问题就来了。
  1. 相同代码在子类中不停重复,写的烦死了,你甚至都懒的使用CV大法。
  2. 代码已经写死,运行时不易改变。
  3. 添加功能时造成其他鸭子不想要的改变。
你思考过后准备重构代码,把易变的模块提取成接口,你的想法如下图。
设计原则:找出变化之处,把他们独立出来。不要和那些不变的东西混在一起。
这样虽然解决了一部分的问题,但是代码还是有重复问题,实现IFly接口的类还是要写关于鸭子飞行的方法,尽管这听起来有点不可思议。
所以你决定再次重构代码。
重构之后的代码增加了代码可重用性,易扩展性,可维护性。
设计原则:要针对接口编程,不要针对实现编程。
  • 针对实现编程:
Dog dog = new Dog(); dog.Dark();
  • 针对接口/父类编程:
Animal animal = new Dog(); animal.MakeSound(); //在运行是才指定具体实现的对象 //我们一点不管他是什么类型,我们只关心他正确的实现MakeSound方法就好了
有了上面的思路,我们需要重新写一下我们的代码,代码如下图。
下边是鸭子行为的接口和此行为的具体类。
/// <summary> /// 所有飞行行为的接口 /// </summary> public interface IFlyBehaviour { void Fly(); }
/// <summary> /// 所有叫行为的接口 /// </summary> public interface IQuackBehaviour { void Quack(); }
using UnityEngine; /// <summary> /// 使用翅膀飞 /// </summary> public class FlyWithWings : IFlyBehaviour { public void Fly() { //使用翅膀飞 Debug.Log("使用翅膀飞"); } }
using UnityEngine; /// <summary> /// 不会飞 /// </summary> public class FlyNoWay : IFlyBehaviour { public void Fly() { //什么都不做,不能飞 Debug.Log("什么都不做,不能飞"); } }
using UnityEngine; /// <summary> /// 呱呱叫 /// </summary> public class Quack : IQuackBehaviour { void IQuackBehaviour.Quack() { //呱呱的叫 Debug.Log("呱呱的叫"); } }
using UnityEngine; /// <summary> /// 吱吱叫 /// </summary> public class Squea : IQuackBehaviour { public void Quack() { //吱吱的叫 Debug.Log("吱吱的叫"); } }
using UnityEngine; /// <summary> /// 不会叫 /// </summary> public class MuteQuack : IQuackBehaviour { public void Quack() { //什么都不做,不会叫 Debug.Log("什么都不做,不会叫"); } }
下面是鸭子的抽象类,关于抽象类需要注意以下几点:
  1. 抽象类只能用做其他类的基类。
  2. 抽象类可以包含抽象成员和普通的非抽象成员。
  3. 抽象可以继承自另一个抽象类
  4. 所有抽象类的成员必须使用override关键字实现。
  5. 抽象类不能创建实例。
/// <summary> /// 鸭子抽象类 /// </summary> public abstract class SuperNewDuck { public IFlyBehaviour flyBehaviour; public IQuackBehaviour quackBehaviour; //必须要实现的可以考虑抽象方法 public abstract void Swim(); public abstract void Display(); public void PerformFly() { //鸭子不会亲自处理飞的行为,而是委托给flyBehaviour引用的对象 flyBehaviour.Fly(); } public void PerformQuack() { //鸭子不会亲自处理呱呱叫的行为,而是委托给quackBehaviour引用的对象 quackBehaviour.Quack(); } public void SetFlyBehaviour(IFlyBehaviour flyType) { flyBehaviour = flyType; } public void SetQuackBehaviour(IQuackBehaviour quackType) { quackBehaviour = quackType; } }
下面是具体的鸭子类。
using UnityEngine; public class SuperMallardDuck : SuperNewDuck { public SuperMallardDuck() { flyBehaviour = new FlyWithWings(); quackBehaviour = new Quack(); } public override void Display() { Debug.Log("外观是野鸭"); } public override void Swim() { Debug.Log("在花式游泳"); } }
using UnityEngine; public class SuperMecoyDuck : SuperNewDuck { public SuperMecoyDuck () { flyBehaviour = new FlyNoWay(); quackBehaviour = new MuteQuack(); } public override void Display() { Debug.Log("外观是木头鸭"); } public override void Swim() { Debug.Log("在正常游泳"); } }
//Unity测试代码... using System.Collections; using System.Collections.Generic; using UnityEngine; public class DuckSimulator : MonoBehaviour { private void Start() { //实例一只野鸭 SuperNewDuck superMallardDuck = new SuperMallardDuck(); superMallardDuck.Display(); superMallardDuck.Swim(); superMallardDuck.PerformFly(); superMallardDuck.PerformQuack(); Debug.Log("--------动态改变鸭子飞行行为-----------"); //运行时改变飞行状态 superMallardDuck.SetFlyBehaviour(new FlyWithWings()); superMallardDuck.PerformFly(); } }
这时新需求来了,要增加一只超级鸭子,他使用的是导弹飞行器,你怎么办?
  1. 添加FlyWithRocket类实现自IFlyBehaviour接口
  2. 给超级鸭子创建FlyWithRocket行为
整理下,最终的代码如下图。
设计原则:多用组合,少用继承。这里的少用并不是不用。
此设计模式名称:策略模式,他定义了算法族,分别封装起来。让他们之间可以互相替换,此算法独立于使用算法的客户。
街头顽禽
Code Man - Programmer
1
Comments