SOLID Principles in Unity

2023-11-06

什么是SOLID?

假如你是一个程序员,我敢肯定你听过编程原则或类似的东西,也许松耦合的设计。
SOLID不是一个编程语言或编程范式
它不是一个特定的设计模式
它不是针对某个项目的

在面向对象编程中,SOLID实际上是一个助记缩写五个(S.O.L.I.D)不同原则旨在使软件设计清晰,更加灵活和易于维护。许多原则和模式的原则是由(罗伯特·c·马丁)提出来的,他们适用于任何面向对象设计,这些的原则也可以形成一个核心的原则用于如敏捷开发或自适应软件开发。

在这里插入图片描述

1-Single Responsibility Principle (SRP)

在软件开发(包括和游戏绝对是这里),只要需求发生的变更,这意味着(可能)的类和组件与一个给定的功能也将受修改参加新规格。在统一中除了要求代码/脚本的变化,很可能也预制,资产和组件改变为了使功能成为可能。一个类拥有太多的职责,它会更难实现一些新特性和维护越来越痛苦,会消耗更多的时间随着项目的发展,增加更多的复杂性和相互责任强耦合的类。

2-Open/Close Principle (OCP)

您应该能够扩展一个类的行为,无需修改

这一原则是实现一个合适的可维护性的构建块创建可重用的组件和对象,这是可能通过创建抽象而不是依赖于具体的东西。这一原则可以遵循两个简单的标准:
(1)对扩展开放,而不是回到相同的类,并开始为每个新特性添加数百行,我们应该做一个类不同按照新的要求。
(2)对修改关闭——你的类不应该改变它的主要行为是由不同部分的代码重用,一定要开始使用接口或抽象类作为特定的组件和基本类型的实体。确保从MonoBehaviour扩展抽象(基地)类和类从基类型也将继承它参加您的需求。

3-Liskov Substitution Principle (LSP)

基类的派生类必须是可替换的

这一原则基本上描述了使用对象引用基类函数/方法应该能够使用从派生类实例没有真正了解它。换句话说,它加强了继承和多态的使用让以后的生活更轻松。让我们举个例子一个类称为DamageManager CalculateDamage接收的方法,一个角色应该收到的伤害。考虑到游戏的要求指定每个字符应受此影响损害不同取决于它的装甲水平和其他属性也在字符类中定义。所以,在一个快速和肮脏的方法,我们可以处理所有的逻辑函数/方法本身,所以我们可以检查字符的类别,它的装甲水平,属性和交易损害这样使用一个条件语句

public class DamageManager
{
    public void CalculateDamage(Character character, int damage)
    {
        private int finalDamage = 0;
    
        if(character.category.Equals(Categories.Warrior))
        {
            finalDamage = damage - character.Armor;
        }
        else if(character.category.Equals(Categories.Mage))
        {
            finalDamage = damage - character.MagicProtection - character.Armor;
        }
        else if(character.category.Equals(Categories.Rogue))
        {
            finalDamage = damage - character.Furtivity - character.Armor;
        }
    
        if(finalDamage > 0)
        {
            character.Life -= finalDamage;
        }
    }
}

不,当然这不是最好的方法来实现此功能,因为这样的代码将继续增长,因为我们最终需要添加额外的字符类别,修饰符,额外的伤害,也许新属性。更不用说,它迫使DamageManager知道更多关于这个角色比真的很有必要,生成一个强耦合的代码,迟早会变成一个意大利面,或者至少痛苦无法维护。
  通过使用Liskov替换原则实际上是容易整理,让它很友好的维护和添加新的东西。首先我们应该创建新的类来表示每个字符类别,它们应该从基类扩展字符。这个角色仍然包含生命属性但现在将有一个方法来描述它的行为在伤害,我们叫它TakeDamage,它将接收一个int损伤和每个新创建的字符类应该实现该方法

public abstract class Character
{
    public int Armor { get; set; }
    public int Life { get; set; }
    
    public abstract void TakeDamage(int damage);
}

public class Warrior : Character
{
    public override void TakeDamage(int damage)
    {
        Life -= Math.Max(0, damage - Armor);
    }
}
public class Mage : Character
{
    public int MagicProtection { get; set; }
    
    public override void TakeDamage(int damage)
    {
        Life -= Math.Max(0, damage - Armor - MagicProtection);
    }    
}

public class Rogue : Character
{
    public int Furtivity { get; set; }
    
    public override void TakeDamage(int damage)
    {
        Life -= Math.Max(0, damage - Armor - Furtivity);
    }    
}

public class DamageManager
{
    public void CalculateDamage(Character character, int damage)
    {
        character?.TakeDamage(damage);
    }
}

4-Interface Segregation Principle (ISP)

许多特定的接口和一个通用接口相比更好

这一原则建议整体界面分解成更小的,例如分离他们的职责作用。假设我们之前使用字符类中提供太阳能发电现在需要实现一个角色攻击,法术铸造和回避行为。之前,开始写这些字符类中的抽象方法稍微思考一下。嘿,我想我们不应该迫使战士类例如有CastSpell方法对吗?是的,那是正确的!通过记住接口隔离原则我们可以开始写一些小型接口作为角色的字符类派生而来。
  所有的字符类应该能够攻击,所以我们可以创建一个接口称为ICanAttack将实现的战士,法师,盗贼类和可能包含一个名为攻击的方法。
  战士更独特,我们可以创建另一个名为ICanCharge的接口,当实现的类

//Interfaces

public interface ICanAttack
{
    void Attack();
}

public interface ICanCharge
{
    void Charge();//Warriors' special attack
}

public interface ICanCastSpell
{
    void CastSpell();//Mages' special attack
}

public interface ICanDodge
{
    void Dodge();//Rogues' special attack
}
//Warrior implementation
public class Warrior : Character, ICanAttack, ICanCharge
{
    public override void TakeDamage(int damage)
    {
        Life -= Math.Max(0, damage - Armor);
    }
    
    public void Attack()
    {
        //Implement here the warrior's simple attack
    }
    
    public void Charge()
    {
        //Implement here the warrior's special attack
    }
}
//Mage implementation
public class Mage : Character, ICanAttack, ICanCastSpell
{
    public int MagicProtection { get; set; }
    
    public override void TakeDamage(int damage)
    {
        Life -= Math.Max(0, damage - Armor - MagicProtection);
    }
    
    public void Attack()
    {
        //Implement here the mage's simple attack
    }
    
    public void CastSpell()
    {
        //Implement here the mage's special attack
    }
}
//Rogue implementation
public class Rogue : Character, ICanAttack, ICanDodge
{
    public int Furtivity { get; set; }
    
    public override void TakeDamage(int damage)
    {
        Life -= Math.Max(0, damage - Armor - Furtivity);
    }
    
    public void Attack()
    {
        //Implement here the rogue's simple attack
    }
    
    public void Dodge()
    {
        //Implement here the rogue's special attack
    }
}

最后一个观点更加遵守所有原则和停止破坏SRP(单一责任原则),我们也可以去beyonde从字符类和分离能力,创建一些额外的类来处理战斗,例如。
  这类需要一个方法接收字符作为参数,也许攻击者攻击角色,或利用多态性通过可能的攻击,区域可能的攻击。这样我们就有类负责处理战斗,只是这样做,这不是一个有责任感的人物了。这将添加一个额外的层复杂性这篇文章,也许我需要进入一些常见的设计模式,以帮助分离,这绝对是超出了本文的范围。但我离开开放的想法来让你们想想,也许自己试用。

5-Dependency Inversion Principle (DIP)

应该依赖于抽象,而不是依赖具体的东西

依赖性倒置原则是由两个基本规则:
  (1)高层模块不应该依赖于低层模块。都应该依赖于抽象。
  (2)抽象不应该依赖于细节。细节应该依赖于抽象。
  为了更好地理解这一原则,让我们来看看下面的类:

public class Sword
{
    public int Damage { get; set; }
}

public class Warrior : Character, ICanAttack, ICanCharge
{
    private Sword sword = new Sword();
    
    public override void TakeDamage(int damage)
    {
        Life -= Math.Max(0, damage - Armor);
    }
    
    public void Attack(Character target)
    {
        if(target != null && sword != null)
        {
            target.TakeDamage(sword.Damage);
        }
    }
    
    public void Charge(Character target)
    {
        if(target != null && sword != null)
        {
            target.TakeDamage(sword.Damage * 2);
        }
    }
}

在上面这个例子中我们可以看到类战士直接取决于类的剑,只是因为在我们虚构的“游戏”使用的剑是初始武器战士。我们可以看到,现在的攻击和充电方法接收一个字符作为参数,考虑到对人物的攻击只会被执行,所以我们只接收目标字符/生物,然后我们经过剑的伤害属性目标通过方法TakeDamage,这里为了简单起见,攻击只是剑造成的破坏,以及在这种情况下,双打损失金额。在这种情况下,接口也会遭受一些变化,接收目标作为参数的方法。
  那么,什么是最大的问题在这个实现中,打破了依赖倒置原则?这个是简单的对吧?是的,武士阶级是根据剑能够执行的具体实施攻击,这是没有那么糟糕,如果我们永远不会有其他的武器

我们来看另外一个例子

public interface IMeleeWeapon
{
    int Damage { get; set; }
}

public class Sword : IMeleeWeapon
{
    public int Damage { get; set; }
}

public class Axe : IMeleeWeapon
{
    public int Damage { get; set; }
}

public class Warrior : Character, ICanAttack, ICanCharge
{
    private IMeleeWeapon weapon;
    
    public void EquipWeapon(IMeleeWeapon weapon)
    {
        this.weapon = weapon;
    }
    
    public override void TakeDamage(int damage)
    {
        Life -= Math.Max(0, damage - Armor);
    }
    
    public void Attack(Character target)
    {
        if(target != null && weapon != null)
        {
            target.TakeDamage(weapon.Damage);
        }
    }
    
    public void Charge(Character target)
    {
        if(target != null && weapon != null)
        {
            target.TakeDamage(weapon.Damage * 2);
        }
    }
}

在第二个例子中,我们可以看出,而不是使用剑战士的依赖,我们现在一个接口IMeleeWeapon,定义了一个破坏财产,需要被定义为所有的类实现接口,如上面我们看到剑和斧头。现在我们的武士阶级仍然可以有武器,但也有可能运动队斧头在需要的时候没有问题。

public class MyGame : MonoBehaviour
{
    private void Awake()
    {
        Warrior warrior = new Warrior();
        
        IMeleeWeapon sword = new Sword();
        IMeleeWeapon axe = new Axe();
        
        warrior?.EquipWeapon(sword); //Equipped a sword
        warrior?.EquipWeapon(axe); //Equipped an axe
    }
}

依赖倒置和依赖注入的一个重要注意的是不一样的,第一个是一个原则,帮助我们解耦类,而第二个是技术用来确保一个给定对象的所有依赖项都交付给该对象,而不需要手动实例化该对象内,作为支持依赖倒置,但不是在这篇文章中解释道。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SOLID Principles in Unity 的相关文章

随机推荐

  • android app缓存机制会自动清除,深入理解Android缓存机制(一)缓存简介

    概述 说起缓存 大家可能很容易想到Http的缓存机制 LruCache 其实缓存最初是针对于网络而言的 也是狭义上的缓存 广义的缓存是指对数据的复用 我这里提到的也是广义的缓存 比较常见的是内存缓存以及磁盘缓存 不过要想进一步理解缓存体系
  • Python内置函数详解,进阶必备

    内置函数就是Python给你提供的 拿来直接用的函数 比如print input等 截止到python版本3 6 2 python一共提供了68个内置函数 具体如下 本文将这68个内置函数综合整理为12大类 正在学习Python基础的读者一
  • 关于SDC时钟约束的事

    1 quartus的SDC约束就跟xilinx的ucf约束文件一样 2 主要用途一般是 一从输入端口到寄存器 二寄存器到寄存器 通过设定时钟频率方式进行约束 三寄存器到输出 四创建时钟约束命令 五时钟延迟约束 六时钟抖动约束 七输入和输出延
  • Android实现简单的相册

    用手机浏览靓照也是件非常惬意的事了 然而如何实现这一功能呢 其实也并不难 下面是一个简单的例子 功能为 主屏幕上显示用户选择的靓照 屏幕下面滚动显示靓照集 点击即可浏览 如下图所示 实现代码为 这里图片没法提供了 可以设置自己喜欢的图片 R
  • MYSQL查询优化器

    MYSQL 逻辑结构 MySQL 使用典型的客户端 服务器 Client Server 结构 体系结构大体可以分为三层 客户端 服务器层以及存储引擎层 其中 服务器层又包括了连接管理 查询缓存 SQL 接口 解析器 优化器 缓冲与缓存以及各
  • Navicat连接Mysql遇到的错误

    在navicat链接mysql以后的版本时 会出现2059的错误 这个错误出现的原因是在mysql8之前的版本中加密规则为mysql native password 而在mysql8以后的加密规则为caching sha2 password
  • mysql内测试连通性命令_怎么使用ping命令进行连通性测试

    关于ping命令的作用 ping 命令有助于验证网络层的连通性 一般进行网络故障排除时 可以使用ping 命令向目标计算机或IP地址发送ICMP回显请求 目标计算机会返回回显应答 如果目标计算机不能返回回显应答 说明在源计算机和目标计算机之
  • python提取txt文件中的数据

    从存储在txt文件中的30000多条数据当中提取自己需要的信息 如下图示例的数据格式 需要提取出now后的数值并存储在另外一个txt文件当中 结果示例 code path r r new data1 txt with open path r
  • C++ 项目

    C 方向 10个企业级别项目 138个工作日 1 学生信息管理系统 C 基础 2 俄罗斯方块 控制台 C 基础
  • Arcgis server在solaris和linux上,用直连的方式连接sde的时的配置

    Arcgis server在solaris和linux上 用直连的方式连接sde的时候需要做一点配置才可以连上 1 在
  • 微信小程序从入坑到放弃二十:生成海报前使用wx.downloadFile或wx.getImageInfo时潜在的坑

    摘要 上周六下午16点左右 公司研发群中扔来一张bug截图 当天21点 群里又扔来bug截图 研发团队二话不说开始查找bug如何复现 22点20分左右 我终于复现了bug 随后就是看源码 搜索 跳坑 爬坑 周日同其他研发小伙伴一样 又是奉献
  • OpenCv案例(九): 基于OpenCvSharp图像分割提取目标区域和定位

    以下原图中 物体连靠在一起 目的是将其分割开 再提取轮廓和定位 原图 最终效果 麻烦的地方是 分割开右下角部分 两个连在一起的目标物体 下图所示 基本方法 BoxFilter滤波 二值化 轮廓提取 凸包检测 图像的矩 代码如下
  • Jar包反编译后修改源码再编译

    Jar包反编译后修改源码再编译 文章目录 Jar包反编译后修改源码再编译 1 场景 2 详细步骤 2 1 查看源码 2 2 生成源文件 2 3 构建项目 2 4 编译成jar包 3 参考链接 1 场景 需要对当前的jar包功能进行修改 但是
  • angularjs系列之轻松使用$q进行异步编程

    angularjs系列之轻松使用 q进行异步编程 来源 网络 编辑 admin 第一部分关于js中的异步编程 异步编程简单的说就是你写了一段代码 但他不会按照你书写代码的顺序立即执行 而是等到程序中发生了某个事件 如用户点击了某个按钮 某个
  • 深入理解计算机系统-链接篇

    在linux系统下 很多开源C C 程序的编译规则都是以makefile文件的形式给出 我刚开始学习makefile规则是看陈浩的 跟我一起学makefile 后来看 深入理解计算机系统 这本书 对编译链接有了更加深入的了解 本文主要介绍程
  • 人机交互知识点总结

    人机交互知识点总结 考试题型及分值分布 选择题 10题 20分 填空题 10题 20分 判断题 可选 5题 10分 解答题 5 6题 30分 分析计算题 1 2题 20分 注意 答案有多条时 用1 2 3形式分别列出 考查内容 掌握人机交互
  • CTF加密解密—CRYPTO—密码学签到

    0x00 考察知识点 考虑字符串倒序的问题 0x01 题目 密文是 wohs ftc galf 0x02 Write Up 直接进行字符串倒序即可 签到题 简单点是肯定的啦
  • 操作系统文件的两种结构

    文件 文件指的是一组带标识的逻辑上有完整意义的数据项 构成文件内容的基本单元 的序列 或者是相关联记录的集合 文件存放在磁盘或磁带等存储介质上 文件的逻辑结构和物理结构 一 逻辑结构 1 无结构文件 流式文件 2 有结构文件 记录式文件 二
  • avue crud-tree介绍

    前言 最近遇到了需要使用表格树的需求 发现avue crud有tree的组件 然后在使用过程中发现诸多问题 网上对应的介绍也很少 所以在自己使用成功之后为后人提供帮助 正文 效果图 html部分
  • SOLID Principles in Unity

    什么是SOLID 假如你是一个程序员 我敢肯定你听过编程原则或类似的东西 也许松耦合的设计 SOLID不是一个编程语言或编程范式 它不是一个特定的设计模式 它不是针对某个项目的 在面向对象编程中 SOLID实际上是一个助记缩写五个 S O