C#之常用设计模式(unity版本)


                                                                  设计模式

一:设计模式之六大原则

六大原则是谁?
①单一职责原则 ②开放封闭原则 ③依赖倒置原则
④里式转换原则 ⑤接口隔离原则 ⑥迪米特原则
六大原则是我们提高面向对象编程代码质量的必备原则,另外还是我们理解设计模式的必备前提。


1.单一职责原则介绍:

单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。简单的说,就是一个类只负责一项职责(功能)。

简单点说:一个类只负责一件具体的事情,一个方法只完成一个特定的功能。当你发现一方法完成了两件事情的时候,就需要适当的重构成两个方法,类也是一样的。

在现实生活中有很多典型的例子体现出了单一职责:

比如:一个房子有客厅,有厨房,有卫生间,有卧室;每个房间都有自己“单一的职责/用途”。
 


2.开放封闭原则介绍:


开放封闭原则:软件实体(类,方法,模块)应该可以扩展,但是不可以修改。开放封闭原则简称为开闭原则。

比如说笔记本电脑:我们购买到的任何品牌的笔记本,都是“开放封闭”的。
封闭:整个笔记本是封闭的,且笔记本背部标明了“非专业人士,请勿试图拆卸
或者维修”以及“撕毁保修无效”的封条。
开放:指的是笔记本提供了若干个 USB 的插口,可供我们扩展。

简单分析:
笔记本本身具备键盘,触摸板,音响等功能,但是这些往往没有独立的外设好。
很多人会选择购买机械键盘,外置鼠标,外置音响,通过 USB 插口对笔记本原
有的功能进行扩展,这是一种正常的操作习惯,扩展而不是修改。
很少有人会在笔记本身上直接进行改动的,比如:把所有的键盘帽翘开,DIY
成机械键盘,拆开机器,更换一个更好的音响。如果你真这样做了,是有很大的
风险的,风险是你有可能破坏笔记本原有的结构,甚至出现大量的潜在风险。

 


3.依赖倒置原则介绍:


依赖倒置原则:针对抽象编程,不要针对实现编程;
高层模块不应该依赖于底层模块,两个模块都应该依赖于抽象(抽象类/接口)。

依赖倒置原则案例:
案例介绍:一个中国人和一个美国人上网,他们都需要搜索,聊天,看视频,但是他们上网的方式不同,比如美国人搜索用Google、聊天用MSN、看视频用Youtube,而中国人搜索用百度、聊天用QQ或者微信、看视频用腾讯或者爱奇艺,这个时候我们就可以引入依赖倒置原则

代码演示:


4.里氏转换原则介绍:


①一个软件实体如果使用的是一个父类的话,那么一定适用于其子类。而且它察
觉不出父类对象和子类对象的区别。
②在软件里面,把父类都替换成它的子类,软件的行为没有变化;简单点说,子
类型必须能够替换掉它们的父类型。

比如说:Unity 引擎是一个父类,Unity4.x,Unity5.x,Unity2017.x 都是这个父类下的子类。本身具备父类
的功能,同时又都有自己的新功能。

里氏转换原则名称的由来:
国外一位姓“里”的女士发表出来的数学理论,该理论最终以发表者的名字命名。
这种以发表者的名字进行命名的概念有很多,比如:牛顿定律,笛卡尔坐标系。


5.迪米特原则介绍:


迪米特原则:也叫做最少知识(知道)原则。
①如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。
如果其中一个类需要调用另外一个类的某一个方法的话,可以通过第三者转发这
个调用。
②一个对象应当对其他对象有尽可能少的了解。

③迪米特原则主要是强调了类与类之间的松耦合。
类与类之间的耦合度越低,越有利于代码的复用,一个处于低耦合的类被修改了,
不会对有关系的类造成影响。

比如说:很多公司的董事长都会有自己的助理(秘书),负责帮自己处理公司的零散事情。
比如说:公司需要开会,如果没有助理,那么董事长需要亲自去通知所有的员工;
但是如果有助理,只需要把开会这件事告诉助理,然后董事长就不需要管了,助
理会去通知所有的员工。
在董事长和公司员工之间,多出来了一个助理,就可以降低董事长和公司员工之
间的耦合度,这样可以提高董事长的时间价值和效率。
没有助理:董事长需要面对 N 个员工,1 对 N 的关系;
有了助理:董事长只需要面对助理 1 人,1 对 1 的关系;然后由助理去 1 对 N。

迪米特原则案例演示:
  使用代码模拟董事长,员工,助理之间的关系。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// EmployeeManager(员工管理器).
/// </summary>
public class EmployeeManager : MonoBehaviour {
 
    private Transform _Transform;
    private GameObject prefab_Employee;
    private List<GameObject> allEmployeeList = new List<GameObject>();
 
    void Start()
    {
        _Transform = gameObject.GetComponent<Transform>();
        prefab_Employee = Resources.Load<GameObject>("Employee");
        CreateAllEmployee();
    }
    /// <summary>
    /// 创建所有员工.
    /// </summary>
    private void CreateAllEmployee()
    {
        for(int i = 0; i < 10; i++)
        {
           GameObject go= GameObject.Instantiate<GameObject>(prefab_Employee,new Vector3(i*1.5f,0f,0f),Quaternion.identity,_Transform);
            allEmployeeList.Add(go);
        }
    }
    /// <summary>
    /// 通知员工开会.
    /// </summary>
    public void SendMessageEmployeeMeeting(string time)
    {
        for(int i = 0; i < allEmployeeList.Count; i++)
        {
            allEmployeeList[i].GetComponent<Employee>().Meeting(time);
        }
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Employee(员工类)
/// </summary>
public class Employee : MonoBehaviour {

    /// <summary>
    /// 接收会议时间的方法.
    /// </summary>
	public void Meeting(string time)
    {
        Debug.Log("我知道了,今天下午"+time+"开会");
    }
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 助理(秘书)类
/// </summary>
public class Secretary : MonoBehaviour {

    private EmployeeManager _EmployeeManager;//持有员工管理器脚本的引用.

    void Start()
    {
        _EmployeeManager = GameObject.Find("EmployeeManager").GetComponent<EmployeeManager>();
    }
    /// <summary>
    /// 通知助理召开会议.
    /// </summary>
    public void SendMessageSecretaryMeeting(string time)
    {
        _EmployeeManager.SendMessageEmployeeMeeting(time);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Boss(老板)类
/// </summary>
public class Boss : MonoBehaviour {

    private Secretary _Secretary;//持有秘书脚本的引用.


    void Start()
    {
        _Secretary = gameObject.GetComponent<Secretary>();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            //通知开会.
            _Secretary.SendMessageSecretaryMeeting("五点");
        }
    }
}

6.接口隔离原则介绍:

接口隔离原则:
①客户端不应该依赖它不需要的接口;
②一个类对另一个类的依赖应该建立在最小的接口上。

接口概念:
接口是一种能力,是一种规范,当我们对现在已经存在的类的继承关系进行功能
扩展的时候,就可以使用接口来完成相应的工作。

生活中的接口隔离原则:

公司内有很多部门,比如:开发部,业务部,财务部等等,每个部门内都有 N
个员工在工作。现在我把员工要做的事情,定义成一个接口,所有员工都实现这
个接口去做事情。
这个接口中定义的事情有:
工资计算,账务管理,客户扩展,客户维护,产品推销,程序开发,软件维护。
所有的员工都实现这个接口,去做事情。现在问题就出现了,不管是哪个部门的
员工,在实现了这个接口后,都有很多事情是不需要自己去做的。
当前这个接口就比较臃肿,我们需要对接口进行“功能隔离”:
财务部员工接口:
工资计算,账务管理;
业务部员工接口:
客户扩展,客户维护,产品推销;
开发部员工接口:
程序开发,软件维护;
这样对接口功能隔离,细分成不同部门的职责功能接口,不管是现有的员工,还
是以后新加入的员工,只需要实现自己部门对应的接口即可。


                                                 设计模式之创建型设计模式 

一:简单工厂模式

简单工厂模式介绍:

①.模式介绍

简单工厂模式[Simple Factory],又叫做静态工厂模式。
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。

②.模式代码结构:

抽象产品角色:简单工厂模式创建的所有对象的父类,用于描述类的公共部分;
具体产品角色:简单工厂模式创建出来的具体产品类;
简单工厂角色:简单工厂模式的核心,实现了创建产品的内部逻辑。
外部可以通过访问“简单工厂”就可以得到具体的产品,而不需要关注产品的具
体创建过程。

测试案例:

实现《90 坦克大战》游戏中的 NPC 坦克。【见图】

NPC 坦克分析:
①在该游戏中有多种不同类型的 NPC 坦克,我们可以用继承关系来对坦克的属
性和行为进行描述;
属性:移动速度,生命值; 行为:移动,射击;
②在游戏中,有一个 NPC 坦克的生成器,我们通过一个按键随机生成一个NPC 坦克。

坦克逻辑实现:

①定义一个TankBase的父类,在父类中实现共有的属性,用虚方法实现行为;
②定义三个坦克 子类,继承父类,重写父类中的虚方法;
③定义一个坦克工厂类,用于实例化和创建坦克 ;

④定义一个坦克管理器,用于坦克的逻辑。

 代码:
 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 所有坦克的父类.
/// </summary>
public class TankBase : MonoBehaviour {

    private int moveSpeed;
    /// <summary>
    /// 移动速度.
    /// </summary>
    public int MoveSpeed
    {
        get { return moveSpeed; }
        set { moveSpeed = value; }
    }

    private int lifeValue;
    /// <summary>
    /// 生命值.
    /// </summary>
    public int LifeValue
    {
        get { return lifeValue; }
        set { lifeValue = value; }
    }

    /// <summary>
    /// 初始化坦克属性.
    /// </summary>
    public void InitTank(int moveSpeed,int lifeValue)
    {
        this.MoveSpeed = moveSpeed;
        this.LifeValue = lifeValue;
    }

    /// <summary>
    /// 坦克移动的虚方法.
    /// </summary>
    public virtual void TankMove()
    {
        Debug.Log("坦克移动");
    }
    /// <summary>
    /// 坦克射击
    /// </summary>
    public virtual void TankShoot()
    {
        Debug.Log("开始射击");
    }
    public override string ToString()
    {
        return string.Format("移动速度:{0},生命值:{1}",this.moveSpeed,this.lifeValue);
    }
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TankA :TankBase {

    public override void TankShoot()
    {
        base.TankShoot();
        Debug.Log("我是坦克A,一次发射一枚炮弹.");
    }

}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TankB : TankBase {

    public override void TankShoot()
    {
        base.TankShoot();
        Debug.Log("我是坦克B,一次发射二枚炮弹.");
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TankC :TankBase {

    public override void TankShoot()
    {
        base.TankShoot();
        Debug.Log("我是坦克C,一次发射三枚炮弹.");
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 坦克工厂类.
/// </summary>
public class TankFctory : MonoBehaviour {

    private GameObject prefab_TankA;
    private GameObject prefab_TankB;
    private GameObject prefab_TankC;

    void Awake()
    {
        prefab_TankA = Resources.Load<GameObject>("TankA");
        prefab_TankB = Resources.Load<GameObject>("TankB");
        prefab_TankC = Resources.Load<GameObject>("TankC");
    }

    /// <summary>
    /// 创建坦克.
    /// </summary>
    public TankBase CreateTank(string tankName)
    {
        TankBase tankBase = null;
        switch (tankName)
        {
            case "TankA":
                tankBase = GameObject.Instantiate<GameObject>(prefab_TankA).GetComponent<TankBase>();
                tankBase.InitTank(2, 100);
                break;
            case "TankB":
                tankBase = GameObject.Instantiate<GameObject>(prefab_TankB).GetComponent<TankBase>();
                tankBase.InitTank(4, 200);
                break;
            case "TankC":
                tankBase = GameObject.Instantiate<GameObject>(prefab_TankC).GetComponent<TankBase>();
                tankBase.InitTank(6, 300);
                break;
        }
        return tankBase;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 所有坦克管理器.
/// </summary>
public class TankManager : MonoBehaviour {

    private List<string> tankList;

    private TankFctory _TankFctory;
    void Start()
    {
        _TankFctory = gameObject.GetComponent<TankFctory>();
        tankList = new List<string>();
        tankList.Add("TankA");
        tankList.Add("TankB");
        tankList.Add("TankC");
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            int index = Random.Range(0, tankList.Count);
            TankBase tankBase = _TankFctory.CreateTank(tankList[index]);
            tankBase.TankMove();
            tankBase.TankShoot();
            Debug.Log(tankBase.ToString());
        }
    }
}

什么是 UML 类图?
UML 类图指的是用图形把类,接口,属性,方法,继承,实现等等面向对象中
的特点和关系,用图形的方式展现出来。

简单工厂模式 UML 简图 [见图]

继承关系:UML 表示为“实线+空心三角”;实线的尾指向的子类,空心三角
指向的是父类。
依赖关系:UML 表示为“虚线+箭头”;虚线的尾指向的是依赖者,箭头指向
的是被依赖者。

二:工厂方法模式

简单工厂模式的缺点
简单工厂模式不符合“开发封闭原则”。
当我们需要增加新的产品的时候,需要对坦克工厂类中增加新的 case 语句块,
现在的坦克工厂类不够“封闭”,且产品量很多的时候,工厂类中的 case 语句
块也会很多。

工厂方法模式介绍:

①.模式介绍
工厂方法模式[Factory Method],该模式定义了一个用于创建对象的接口,让
子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
工厂方法模式是对简单工厂模式的进一步抽象和升级,但绝不是替代。


②.模式代码结构
抽象产品角色:创建的所有对象的父类,用于描述类的公共部分;
具体产品角色:创建出来的具体产品类;
抽象工厂角色:工厂的抽象父类,定义生成产品的方法;
具体工厂角色:抽象工厂角色的具体子类,每一个具体产品都对应一个工厂。

工厂方法模式演示:

①定义一个坦克工厂接口,接口内实现一个生成坦克的方法;
②为每一个坦克子类都创建一个针对性的工厂类,该工厂类需要实现坦克接口;
③客户端脚本使用工厂方法模式。
 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 坦克工厂接口.
/// </summary>
interface ITankFactory {
 
    TankBase CreateTank(GameObject go);
}

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 坦克A工厂类.
/// </summary>
public class TankAFactory : ITankFactory
{
    public TankBase CreateTank(GameObject go)
    {
        TankBase tankBase= GameObject.Instantiate<GameObject>(go).GetComponent<TankBase>();
        tankBase.InitTank(2,100);
        return tankBase;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 坦克B工厂类.
/// </summary>
public class TankBFactory : ITankFactory
{
 
 
    public TankBase CreateTank(GameObject go)
    {
        TankBase tankBase = GameObject.Instantiate<GameObject>(go).GetComponent<TankBase>();
        tankBase.InitTank(4, 200);
        return tankBase;
    }
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值