设计模式和设计原则

六大设计原则

单一职责原则 (SRP)

只做一件事,不要所有事都掺乎到一起

一个模块只做一件事   按功能分
一个类只做一件事     一个脚本就放一个类 一个类制作一个功能 也看代码体量
一个方法只做一件事

多态是解决分支的好办法

using UnityEngine;

/*
	有两种攻击方式 一种近战 一种远程
*/

#region 没有遵循单一职责的
    
public enum HeroType {
    CloseCombat,//近战
    LongDistance//远程
}
    
public class Hero {
    private HeroType heroType;
    
    public void Attack(){
        if(heroType == HeroType.CloseCombat){
            Debug.Log("近战攻击");
        }
        else if(heroType == HeroType.LongDistance){
            Debug.Log("远程攻击");
        }
    }
}
#endregion
  
#region 遵循单一职责的
    
public class HeroBase {
    //不同英雄攻击方式是不一样的
    public virtual void Attack(){
        Debug.Log("攻击");
    }
}

//创建子类继承HeroBase
public class CloseCombatHero : HeroBase {
    //近战英雄重写继承过来的Attack
    public override void Attack(){
        //要不要父类里的内容 看情况
        //base.Attack(); 执行父类的
        Debug.Log("近战攻击");
    }
}

public class LongDistanceHero : HeroBase {
    public override void Attack(){
        Debug.Log("远程攻击");
    }
}
#endregion

里氏替换原则(LSP)

使用抽象和多态

父类有的,只要是public 子类都能访问得到 类的强继承性

里氏替换是将 不合理的继承关系就要断掉

多态里不要用关键词new 覆盖 要重写的话 父类用abstruct virtual关键词

using UnityEngine;

/*
	开启瞄准镜的功能 手枪没有开镜功能
*/

#regine 没有遵循里氏替换的
public class Gun {
    //开启瞄准镜
    public virtual void OpenSight(){
        
    }
}

/*
	这里不合理 解决办法是再创建一个类 把开镜的功能放入 这样就继承不到了
*/

public class Pistol : Gun {
    //手枪没有开镜功能 这样就不合理 
    public override void OpenSight(){
        base.OpenSight();
    }
}
#endregine

迪米特法则(LKP)

越少知道越好 只和最密切的联系 迪米特法则让我们遵循的是高内聚 低耦合

比如: A-----B-----C-----D

A只和B联系 B只和C联系 C只和D联系

UML类图 可以显示出来 具体的关系

using UnityEngine;

#regine 没有遵循迪米特法则的
public class Student {
    public string name;
    public int age;
    //这样写 如果这时候我需要变更 Student里的内容 ShowMe()方法都需要动 这是跨越性的改动
}

//年级
public class Grade {
    public Student[] students;
}

public class School {
    public Grade[] grades;
    
    public void ShowMe(){
        for(int i = 0; i < grades.Length; i++){
            for(int j = 0; j < grades[i].students.Length; j++){
            	Debug.Log("name: " + grades[i].students[j].name);
                Debug.Log("age: " + grades[i].students[j].age);
        	}
        }
    }
}
#endregine
   
    
    
//修改    
#regine 遵循迪米特法则的
public class Student {
    private string name;
    private int age;
    private string hobby;
    
    public void ShowMe(){
        Debug.Log("name: " + grades[i].students[j].name);
        Debug.Log("age: " + grades[i].students[j].age);
        Debug.Log("hobby: " + grades[i].students[j].hobby);
    }
}

//年级
public class Grade {
    private string gradeNumber;
    private Student[] students;
    
    public void ShowMe(){
        Debug.Log("轮到" + gradeNumber + "介绍");
        
        for(int i = 0; i < students.Length; i++){
            students[i].ShowMe();
        }
    }
}

public class School {
    private string schoolName;
    private Grade[] grades;
    
    public void ShowMe(){
        Debug.Log("该" + schoolName + "介绍");
        
        for(int i = 0; i < grades.Length; i++){
            grades[i].ShowMe();
        }
    }
}
#endregine

依赖倒置原则(DIP)

面向抽象编程

using UnityEngine;
using System.Collections.Generic;

public abstract class ABGun {
    public abstract void Fire();
}

public class AK47 : ABGun {
    public override void Fire(){
        Debug.Log("哒哒哒");
    }
}

public class MP5 : ABGun {
    public override void Fire(){
        Debug.Log("突突突");
    }
}

接口隔离原则(ISP)

using UnityEngine;

interface IBaseOperation{
    void Move();
    void Jump();
}

interface IFire{
    void Fire();
}

interface IUseSkill{
    void UseSkill();
}

interface IBaseRole : IUseSkill, IFire, IBaseOperation {
    
}

class Role : IUseSkill, IFire, IBaseOperation {
    public void UseSkill(){
    }
    public void Fire(){
    }
    public void Move(){
    }
    public void Jump(){
    }
}

//这时候我假如只用的到两个 我也不能删 只能空着
class Player : IBaseRole {
    public void UseSkill(){
    }
    public void Fire(){
    }
    public void Move(){
    }
    public void Jump(){
    }
}


#region 使用接口隔离原则
class Player : IBaseOperation {
    public void Move(){
    }
    public void Jump(){
    }
}
#endregion

开闭原则(OCP)【总则】

对拓展是开放的,对修改是关闭的

当框架写完之后 如果发生需求的变更 就需要拓展 不要修改(增加需求时候)

在原代码的基础上 不动原来的代码,新增加一个类,来写新的功能

设计模式

GoF 《设计模式》

简单工厂模式

经常调用的

using UnityEngine;

#regine 没有遵守单一原则的
//这样写没有遵守单一原则 如果要添加乘法除法,那就要在原来的代码上去添加新的代码 
public class CountFactory {
	public void GetResult(int num01,int num02,string sign){
        switch(sign){
            case "+":
               return num01 + num02;
            case "-":
               return num01 - num02;    
            default:
               return 0;
        }
    }
}
#endregine
    
	   
/*
	把加减乘除分别做成类,特点是无论加减乘除都需要结果 抽象
	这里改下需求比如需要QWER
*/
    
    
public class MyHero {
  	public float magic;
    public float[] cds;
}
    
    
public abstract class Skill {
    //技能编号
    private string sign = "Q";
    public int skillIndex = 0;
    //技能cd
    private float cd;
    //耗蓝
	protected float magicNeed;
    
    public abstract void SkillRelease();
}

public class WWW : Skill {
	//释放的坐标
    public Vector3 pos;
    
    public override void SkillRelease(){
    	skillIndex = 1;
        
        //释放就不判断范围了 
        if (pos != null){
            Debug.Log("使用技能");
        }
    }
}

public class QQQ : Skill {

    public MyHero targetHero;
    
    public override void SkillRelease(){
    	skillIndex = 0;
        
        //蓝够 cd转完了 目标不是空
        if (targetHero.magic >= this.magicNeed && 
            targetHero.cds[skillIndex] == 0 && targetHero != null){
            Debug.Log("使用技能");
        }
    }
}

//技能都写好了 写工厂
public class SkillOperationFactory {
    public static Skill GetSkillOperation(string sign){
        if (sign == "Q"){
            return new QQQ();
        }
        else{
            return new WWW();
        }
    }
}

public class SimplerFactoryDemo : MonoBehaviour {
    private void Start(){
        Skill skill = SkillOperationFactory.GetSkillOperation("Q");
        skill.SkillRelease();
    }
}
关于Resources的缺点

Unity里 Resources.Load<> 里有一个缺点 文件是存在硬盘里的 但是每次加载到内存都会开辟一块新的空间,造成浪费,而以前加载的还在

解决办法 缓存机制

整理工具库

using UnityEngine;
using System.Collections.Generic;//引入命名空间

//通用框架
namespace Utility {
    
    //通用工厂 获取各种资源
    //资源管理器
    public class AssetsManager : Singleton<AssetsManager> {
        //非第一次的加载要从缓存里加载

        //构造 里实例化
        protected AssetsManager(){
            //key 路径 value 资源
            assetsCache = new Dictionary<string,Object>();
        }
        
        //声明一个缓存池  缓存字典
        private Dictionary<string,Object> assetsCache;
       
        //只有第一次的会加载,后面的都直接从缓存池里加载
        public virtual T GetAssets<T>(string path) where T : Object{
            //先查看缓存池有没有这个资源
            if(assetsCache.ContainsKey(path)){
                //如果有直接返回  将缓存池里的资源返回
                return assetsCache[path] as T;
            }
            else{
                //通过Resources.load加载资源
                T assets = Resources.Load<T>(path);
                //将新资源放到缓存池里
                //				key   value
                assetsCache.Add(path,assets);
                //返回资源
                return assets;
            }
        }
        
        //卸载未使用的资源
        public void UnloadUnuseAssets(){
            Resources.UnloadUnusedAssets();
        }
        
    }
    

    //通过预设体获取
    public class PrefabManager : Singleton<PrefabManager> {
        private PrefabManager(){
        	
        }
        
        //生成  获取预设体生成资源
        public GameObject CreateGameObjectByPrefab(string path){
            //获取预设体
            GameObject prefab = AssetsManager.GetInstance().GetAssets<GameObject>(path);
            //生成
            GameObject obj = Object.Instantiate(prefab);
            //返回
            return obj;
        }
        
        //重载  增加坐标旋转
        public GameObject CreateGameObjectByPrefab(string path,Vector3 pos,Quaternion qua){
            GameObject obj = CreateGameObjectByPrefab(path);
            /*
            //获取预设体
            GameObject prefab = base.GetAssets<GameObject>(path);
            //生成
            GameObject obj = Instantiate(prefab);
            */
            //设置坐标和旋转
            obj.transform.position = pos;
            obj.transform.rotation = qua;
            //返回
            return obj;
        }
        
        //重载  增加父对象  增加缩放
        public GameObject CreateGameObjectByPrefab(string path,Transform parent,Vector3 localPos,Quaternion localQua){
            GameObject obj = CreateGameObjectByPrefab(path);
            /*
            //获取预设体
            GameObject prefab = base.GetAssets<GameObject>(path);
            //生成
            GameObject obj = Instantiate(prefab);
            */
            //设置父物体	
            obj.transform.SetParent(parent);
            //设置坐标和旋转
            obj.transform.localPosition = localPos;
            obj.transform.localRotation = localQua;  
            //返回
            return obj;
        }

		public GameObject CreateGameObjectByPrefab(string path,Transform parent,Vector2 anchoredPosition){
			//生成
            GameObject obj = CreateGameObjectByPrefab(path);
            //设置父物体	
            obj.transform.SetParent(parent);
            obj.transform.localScale = Vector3.one;
            obj.GetComponent<RectTransform>().anchoredPosition = anchoredPosition;
            //返回
            return obj;
		}

		//这个重载用于UI显示的
		public GameObject CreateGameObjectByPrefab(string path,Transform parent,Vector3 localPos,Vector3 localScale){
			//生成
            GameObject obj = CreateGameObjectByPrefab(path);
            //设置父物体	
            obj.transform.SetParent(parent);
            //调整UI显示位置 缩放
            obj.transform.localPosition = localPos;
            obj.transform.localScale = localScale;
            //返回
            return obj;
        }
    }
}

策略模式

解决一件事的多种策略

英雄攻击的三种策略

圆形攻击 矩形攻击 扇形攻击

using UnityEngine;

namespace StrategyMode {
    public class Hero {
        private string name;
        public Transform heroTra;
        public float hp = 100;


        public Hero(string name,Transform heroTra){
            this.name = name;
            this.heroTra = heroTra;
        }

        public void Attack(AttackStrategy attackStrategy,Hero targetHero){
            //被攻击的英雄
            float damage = attackStrategy.TakeDamage(targetHero);
            //受到伤害
            targetHero.TakeDamage(damage);
        }

        public void TakeDamage(float damage){
            hp -= damage;
            //DOTO:死亡啥啥的
        }
    }

    //攻击策略 (基类)
    public abstract class AttackStrategy {
        /*
            矩形:中心点,长度,宽度,释放的位置 坐标
            圆形:中心,半径
            扇形:方向,角度
        */

        public abstract float TakeDamage(Hero hero);
    }

    ****************************************************************************************

    #regine 圆形
    //圆形
    public class CircleAttack : AttackStrategy {

        //需要个点
        //中心点
        private Vector3 skillCenter;
        //半径
        private float skillRadius;

        //这里就看游戏怎么定的了 这里是递减
        //中心伤害
        private float centerDamage;
        //边缘伤害
        private float edgeDamage;

        public CircleAttack(Vector3 skillCenter,float skillRadius,
                            float centerDamage,float edgeDamage){
            this.skillCenter = skillCenter;
            this.skillRadius = skillRadius;
            this.centerDamage = centerDamage;
            this.edgeDamage = edgeDamage;
        }

        public override float TakeDamage(Hero hero){
            //判断英雄位置在没在圈里
            //判断圆心到英雄的距离 是否大于半径

            //计算玩家(英雄)与技能中心点之间的距离	
            float dis = Vector3.Distance(hero.heroTra.position,skillCenter);
            //如果这个距离 小于半径(在圈里)那就有伤害 大于半径(圈外边)那就没伤害
            if(dis <= skillRadius){
                /*
                    比如中心点伤害100 最边上伤害10 半径长度10 当前英雄所在位置8  
                    (10-8)/10*(100-10)=18   18+10(这个10是最边上的伤害)=28
                */

                return edgeDamage + (skillRadius - dis) / skillRadius * (centerDamage - edgeDamage);
            }
            else{
                return 0;
            }
        }
    }
    #endregine

    ****************************************************************************************   
    #regine 矩形
    public class RectangleAttack : AttackStrategy {
        //中心点
        private Vector3 skillCenter;
        /*
            矩形 在长方形范围里 就有伤害 不在没伤害

            可以画个图 中心点到英雄连一条线 然后投影 
            投影的长 小于 长度的一半
            投影的宽 小于 宽度的一半 
            那就在矩形里

            有一个不满足就不在里边

            这里没有设置玩家释放的方向 都是朝着正面释放
        */

        private float width;//宽
        private float height;//高
        //伤害值 如果在这个范围就有伤害
        private float damage;
        //技能释放者
        private Hero releaseHero;


        public RectangleAttack(Vector3 skillCenter,float width,float height,
                               float damage,Hero releaseHero){
            this.skillCenter = skillCenter;
            this.width = width;
            this.height = height;
            this.damage = damage;
            this.releaseHero = releaseHero;
        }

        public override float TakeDamage(Hero hero){
            //方向向量
            //技能中心指向英雄
            Vector3 dir = hero.heroTra.position - skillCenter;
            //求一个向量在某一个向量上的投影
            //方向向量在释放者水平方向的投影向量
            Vector3 horDir = Vector3.Project(dir,releaseHero.heroTra.right);
            //方向向量在释放者垂直方向的投影向量
            Vector3 verDir = Vector3.Project(dir,releaseHero.heroTra.forward);
            //算距离
            if(horDir.magnitude <= width && verDir.magnitude <= height){
                return damage;
            }
            return 0;
        }
    }
    #endregine    

    ****************************************************************************************     

    #regine 扇形
    public class FanshapedAttack : AttackStrategy {
        //中心点
        private Vector3 skillCenter;
        //伤害值 如果在这个范围就有伤害
        private float damage;
        //技能释放者
        private Hero releaseHero;
        //扇形技能的角度
        private float angle;
        //扇形半径
        private float skillRadius;

        /*
            先算英雄所在位置的长度是否超过半径	
            然后再看范围是不是在技能的角度里
                    判断方法 中心点 和 英雄做一条连线 判断夹角是否小于扇形夹角的一半
        */


        public RectangleAttack(Vector3 skillCenter,float angle,
                               float damage,Hero releaseHero){
            this.skillCenter = skillCenter;
            this.angle = angle;
            this.damage = damage;
            this.releaseHero = releaseHero;
        }

        public override float TakeDamage(Hero hero){
            //要判断两个 一个是在没在范围内
            //另一个是 是否超过角度
            //距离                         被攻击的英雄            释放技能的英雄
            float dis = Vector3.Distance(hero.heroTra.position,releaseHero.heroTra.position);
            //夹角                              释放者自身前方
            float angle = Vector3.Angle(releaseHero.heroTra.forward,hero.heroTra.position - releaseHero.heroTra.position);
            //超出距离
            if (dis > skillRadius)
                return 0;
            //不在夹角范围内
            if (angle > this.angle / 2)
                return 0;
            return damage;
        }
    }
    #endregine    


    public class StrategyDemo : MonoBehaviour {
        //释放技能的英雄
        public Transform releaseHero;
        //受伤的英雄
        public Transform hurtHero;

        private void Start(){
			Hero aaa = new Hero("aaa",releaseHero);
            Hero bbb = new Hero("bbb",hurtHero);
            
            //攻击的策略
            
            CircleAttack circleAttack = new CircleAttack(Vector3.zero,10,100,5);
            //圆形攻击
            aaa.Attack(circleAttack,bbb);
            
        }
    }
}

装饰模式

基础功能以外的拓展功能 这个在unity里用的比较少

工厂方法模式

与简单工厂的区别 工厂抽象 让子类去实现内容

原型模式

模板克隆一份

模板方法模式

抽象模板类 子类实体

外观模式

有很多功能,只留一个外部接口

unity场景里就摄像机 灯光 顶多在留有一个空对象 外观作为启动窗口 运行后就都显示出来了

using UnityEngine;
using Utility;//引用之前写的资源管理框架

public class FacadeDemo : MonoBehaviour {
	private void Start(){
        //“这里写的是预设体的名字”,预设体要作为画布的子物体,当前脚本挂在画布上
		PrefabManager.GetInstance().CreateGameObjectByPrefab(
            "RedPanel",transform,Vector3.zero,Quaternion.identity);
	}
}

建造者模式

复杂对象构建

观察者模式

持续观察 做某件事 通知者委托观察者观察,条件成立了 观察者通知给通知者

using UnityEngine;
using System.Collections.Generic;

//观察者等待消息
public class ObserverWaitMsg {
    
    public string name;
    
    public ObserverWaitMsg(string name){
        this.name = name;
    }
    
    //接受消息
    public void ReceiveMsg(string msg){
        Debug.Log(name + "收到了消息:" + msg);
    }
}

//通知者发送消息
public class SubjectSendMsg {
    
    //通知的列表
    private IList<ObserverWaitMsg> Observers;
    
    //构造函数实例化
    public SubjectSendMsg(){
        Observers = new List<ObserverWaitMsg>();
    }
    
    //添加列表
    public void AddObserver(ObserverWaitMsg observer){
        //判断是否有该观察者
        if(!observers.Contains(observer)){
            observers.Add(observer);
        }
    }
    
    //移除列表
    public void RemoveObserver(ObserverWaitMsg observer){
        //判断是否有该观察者
        if(observers.Contains(observer)){
            observers.Remove(observer);
        }
    }
    
    //通知
    public void Notify(string msg){
        for(int i = 0;i < observers.Count;i++){
            //通知给每个观察者
            observers[i].ReceiveMsg(msg);
        }
    }
    
    //检测时间是不是够了
    //观察游戏时间
    public void WatchGameTime(float targetTime,float currentTime){
        if(currentTime >= targetTime){
            Notify("挂机任务完成");
        }
    }
}

public class ObserverDemo : MonoBehaviour {
    
    //普通任务观察者
    private ObserverWaitMsg normalTask;
    //成就任务观察者
    private ObserverWaitMsg achievementTask;
    //通知者
    private SubjectSendMsg subject;
    
    private void Start(){
        normalTask = new ObserverWaitMsg("普通任务观察者");
        achievementTask = new ObserverWaitMsg("成就任务观察者");
        subject = new SubjectSendMsg();
        //添加观察者到观察者列表
        subject.AddObserver(normalTask);
        subject.AddObserver(achievementTask);
        //移除成就任务观察者
        subject.RemoveObserver(achievementTask);
    }
    
    private void Update(){
        subject.WatchGameTime(5,Time.time);
    }
}

代理模式

using Engine;
using System;

//任务类
public class Task {
   
    private int taskTargetCount;
    private int currentCount;
    
    //任务目标
    public void SetTarget(int targetCount){
        this.taskTargetCount = targetCount;
    }
    
    
    public void TaskCallback(){
        currentCount++;
        Debug.Log("检测抽了一张牌(" + currentCount + "/" + taskTargetCount + ")")
        
        if(currentCount == taskTargetCount){
            Debug.Log("抽卡任务完成");
        }
    }
}

public class CardManager {
    //委托
    public Action getCardCallbacks;
    
    public void AddGetCardListener(Action action){
        if(getCardCallbacks == null){
            getCardCallbacks = action;
        }
        else{
            getCardCallbacks += action;
        }
    }
    
    public void RemoveGetCardListener(Action action){
        try{
        	getCardCallbacks -= action;
        }
        catch (Exception e){
            Debug.LogWarning(e);
        }
    }
    
    public void GetOneCard(){
        if(getCardCallbacks != null)
        	getCardCallbacks();
    }
}


public class ProxyDemo : MonoBehaviour {
    
    private CardManager cardManager;
    
	private void Start(){
        cardManager = new CardManager();
        Task task = new Task();
        
        task.SetTarget(5);
        //绑定监听
        cardManager.AddGetCardListener(task.TaskCallback);
    }
    
    private void Update(){
        if(Input.GetKeyDown(KeyCode.Space)){
            //抽一张
            cardManager.GetOneCard();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值