Unity【话大】设计模式之面向对象七大原则

简介: 首先放出早先写的面向对象七大原则,以前不了解的同学建议先大概看一遍~有说的不正确或者不准确的地方欢迎留言指正有什么有趣的写作技巧或者想法欢迎大家给我留言,大家的帮助是我写下去最有效的动力下面笔者跟大家聊一聊每一条原则到底是个什么东东[1]单一职责原则(Single Responsibility Principle)见名知意,这个条职责的潜台词的就是,专注做一个事、专注做一个事、专注做一个事,重要的事说三遍。

首先放出早先写的面向对象七大原则,以前不了解的同学建议先大概看一遍~



有说的不正确或者不准确的地方欢迎留言指正


有什么有趣的写作技巧或者想法欢迎大家给我留言,大家的帮助是我写下去最有效的动力



下面笔者跟大家聊一聊每一条原则到底是个什么东东



[1]

单一职责原则(Single Responsibility Principle)

见名知意,这个条职责的潜台词的就是,专注做一个事、专注做一个事、专注做一个事,重要的事说三遍。别的事情我都不管,我只负责我的事情,专注干好自己的东西。

那单一职责用在程序中有什么好处呢?

  • 提高可读性,想想10个功能放在一个类里面写方便阅读还是分10个类方便阅读?
  • 出现BUG时可以缩小出错的范围,更好的定位,哪个功能出错找哪个功能,早日和BUG说ByeBye~
  • 修改逻辑时避免造成其他功能模块错误,例如折扣数值更改0.8,你不小心把极品装备爆率改成0.8,那你就等死吧


[2]

里氏替换原则(Liskov Substitution Principle)

通俗的讲就是老子不在可以用儿子顶替,具体这条原则怎么个好法请看下面的示例

基础代码
public abstract class  Phone
{
    public abstract void Call();
}
 interface Android{ }
 interface IOS{ }
public class OnePlus : Phone, Android
{
    public override void Call()
    {
        Debug.Log($"{nameof(OnePlus)}进行通话。。。。。");
    }
}

public class Pixel : Phone, Android
{
    public override void Call()
    {
        Debug.Log($"{nameof(Pixel)}进行通话。。。。。");
    }
}
public class XiaoMi : Phone, Android
{
    public override void Call()
    {
        Debug.Log($"{nameof(XiaoMi)}进行通话。。。。。");
    }
}

public class Apple : Phone, IOS
{
    public override void Call()
    {
        Debug.Log($"{nameof(Apple)}进行通话。。。。。");
    }
}

不使用里氏替换,调用call函数需要为每个类型的手机写一个函数

    public void WantToCall_0(OnePlus phone)
    {
        phone.Call();
    }

    public void WantToCall_1(Pixel phone)
    {
        phone.Call();
    }

    public void WantToCall_2(XiaoMi phone)
    {
        phone.Call();
    }

    public void WantToCall_3(Apple phone)
    {
        phone.Call();
    }

使用里氏替换,只需要一个函数全部搞定,在后面的【桥接模式】会广泛用到的

    public void WantToCall_4(Phone phone)
    {
        phone.Call();
    }


[3]

依赖倒置原则(Dependence Inversion Principle)

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 依赖倒置原则的核心思想是面向接口编程。

下面我们先解释接个名词,什么是高层模块,什么是低层模块?什么是细节,什么是抽象?

在项目中我们经常会有一些数学函数库,或者工具类(Log日志工具),这些封装好的工具会被我们业务逻辑模块经常调用,那么这些工具函数库就是低层模块,业务逻辑模块就是高层模块。

细节的意思就是具体的实现,例如上面里氏替换中打电话的例子,在不使用里氏替换的时候需要定义4种打电话函数,这就是依赖细节,其中的细节就是“OnePlus ”、“Pixel ”、“XiaoMi”、“Apple ”,反之抽象就是Phone

下面笔者就举一个例子来说明,示例是这样的,华硕和微型都可使用不同型号的显卡,反之不同型号的显卡也可以使用在不同品牌的主板上。利用依赖倒置原则,这种规则的实现变得很简单也很灵活~

基础代码

//显卡
public interface IGraphicsCard
{
    void BeginWork(IMainboard mainboard);
}
//主板
public interface IMainboard
{
    void GetElectricity();
    void DrawPicture(IGraphicsCard graphicsCard);
}


public class NVIDIA_2018Ti : IGraphicsCard
{
    public NVIDIA_2018Ti(IMainboard mainboard)
    {
        BeginWork(mainboard);
    }
    public void BeginWork(IMainboard mainboard)
    {
        mainboard.GetElectricity();
        Debug.Log($"NVIDIA_2018Ti获取{mainboard.GetType()}电量后开始工作");
    }
}
public class NVIDIA_2018 : IGraphicsCard
{
    public NVIDIA_2018(IMainboard mainboard)
    {
        BeginWork(mainboard);
    }
    public void BeginWork(IMainboard mainboard)
    {
        mainboard.GetElectricity();
        Debug.Log($"NVIDIA_2018获取{mainboard.GetType()}电量后开始工作");
    }
}

//华硕主板
public class Asus : IMainboard
{
    public void DrawPicture(IGraphicsCard graphicsCard) { }
    public void GetElectricity() { }
}
//微型主板
public class MSI : IMainboard
{
    public void DrawPicture(IGraphicsCard graphicsCard) { }
    public void GetElectricity() { }
}

实现代码,这种2*2种模式的实现非常简单~

    public void DrawPicture()
    {
        IMainboard aSus = new Asus();
        aSus.DrawPicture(new NVIDIA_2018Ti(aSus));
        aSus.DrawPicture(new NVIDIA_2018(aSus));

        IMainboard mSI = new MSI();
        mSI.DrawPicture(new NVIDIA_2018Ti(mSI));
        mSI.DrawPicture(new NVIDIA_2018(mSI));
    }


[4]

接口隔离原则(Interface Segregation Principle)

通俗的讲就是定义的接口尽量按照功能细分,比如打电话功能一个接口,上网一个接口,发短信一个接口,分的够细不仅职能明确,也不会因为使用某种职能必须实现一些没必要的功能。总之通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。

我们可以先参考下微软他们的做法,都是按职能划分,而且划分的很细

img_d7db5e117e78a2f4b97e5b24a3ee54ec.png


[5]

迪米特法则(Law Of Demeter)也叫【最少知识原则】

他想表达的意思是能用 private、protected的就不要用public,不要过多的暴露自己的内容,而且对应类与类之间的关系,尽量越少越好。后面讲到的【门面模式】和【中介者模式】想表达的也是这个意思。

迪米特法则其根本思想,是强调了类之间的松耦合。类之间的耦合越弱,一个处于弱耦合的类被修改,不会对有关系的类造成波及。

在下面的示例中说明的是GameObject扩展Log方法都写在了Object上面,这种情况就违背了迪米特法则。因为其他的类不需要这个Log扩展,这也就破坏了原来的结构,侵入性太强了。如果所有的扩展都是以Object为基准,那么调用函数的时候就会出现数不过来的函数下拉条目~~~~

public static class Exted
{
    public static void CustomerLog_Obj0(this GameObject Obj) { }

    public static void CustomerLog_Obj1(this object Obj) { }
    public static void CustomerLog_Obj2(this object Obj) { }

    public static void CustomerLog_Obj3(this object Obj) { }
    public static void CustomerLog_Obj4(this object Obj) { }
}
img_9a6219d8e2787139fe570debd6b21a33.png


[6]

开闭原则(Open Close Principle)

开放封闭原则主要体现在对扩展开放、对修改封闭,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况.封装变化,是实现开放封闭原则的重要手段,对于经常发生变化的状态,一般将其封装为一个抽象,拒绝滥用抽象,只将经常变化的部分进行抽象。

通俗的讲在功能变动的时候,尽量以增量补丁的形式更改,也就是原来的东西尽量原封不动,继承原来的东西在添加。比如原来的东西作为父类,新的更改在子类里面实现。



[7]

组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)

引用老版本的话~

因为:其实整个设计模式就是在讲如何类与类之间的组合/聚合。在一个新的对象里面通过关联关系(包括组合关系和聚合关系)使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用其已有功能的目的。也就是,要尽量使用类的合成复用,尽量不要使用继承。
如果为了复用,便使用继承的方式将两个不相干的类联系在一起,违反里氏代换原则,哪是生搬硬套,忽略了继承了缺点。继承复用破坏数据封装性,将基类的实现细节全部暴露给了派生类,基类的内部细节常常对派生类是透明的,白箱复用;虽然简单,但不安全,不能在程序的运行过程中随便改变;基类的实现发生了改变,派生类的实现也不得不改变;从基类继承而来的派生类是静态的,不可能在运行时间内发生改变,因此没有足够的灵活性。
所以:组合/聚合复用原则可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。


  1. 单一职责原则脚注结尾

  2. 里氏替换原则脚注结尾

  3. 依赖倒置原则脚注结尾

  4. 接口隔离原则脚注结尾

  5. 迪米特法则脚注结尾

  6. 开闭原则脚注结尾

  7. 组合/聚合复用原则脚注结尾

相关文章
|
3月前
|
设计模式 数据库连接 PHP
PHP编程中的面向对象与设计模式
在PHP编程世界中,掌握面向对象编程(OOP)和设计模式是提升代码质量和开发效率的关键。本文将深入浅出地介绍如何在PHP中应用OOP原则和设计模式,以及这些实践如何影响项目架构和维护性。通过实际案例,我们将探索如何利用这些概念来构建更健壮、可扩展的应用程序。
|
3月前
|
设计模式
设计模式七大原则
这篇文章介绍了设计模式中的七大原则,特别强调了单一职责原则,即一个类应该只有一个引起其行为变化的原因,以确保类功能的高内聚和低耦合。
|
3月前
|
设计模式 存储 前端开发
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
|
2月前
|
设计模式 Java 关系型数据库
设计模式——设计模式简介和七大原则
设计模式的目的和核心原则、单一职责原则、接口隔离原则、依赖倒转原则、里氏替换原则、开闭原则、迪米特法则、合成复用原则
设计模式——设计模式简介和七大原则
|
3月前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
79 0
|
3月前
|
设计模式 算法 开发者
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
|
3月前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
|
3月前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是设计模式,单一职责原则如何理解
React开发设计模式及原则概念问题之什么是设计模式,单一职责原则如何理解
|
3月前
|
图形学 C#
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
176 6
|
2月前
|
测试技术 C# 图形学
掌握Unity调试与测试的终极指南:从内置调试工具到自动化测试框架,全方位保障游戏品质不踩坑,打造流畅游戏体验的必备技能大揭秘!
【9月更文挑战第1天】在开发游戏时,Unity 引擎让创意变为现实。但软件开发中难免遇到 Bug,若不解决,将严重影响用户体验。调试与测试成为确保游戏质量的最后一道防线。本文介绍如何利用 Unity 的调试工具高效排查问题,并通过 Profiler 分析性能瓶颈。此外,Unity Test Framework 支持自动化测试,提高开发效率。结合单元测试与集成测试,确保游戏逻辑正确无误。对于在线游戏,还需进行压力测试以验证服务器稳定性。总之,调试与测试贯穿游戏开发全流程,确保最终作品既好玩又稳定。
103 4