2019.9.01 五大基本原则

单一职责原则SRP(Single Responsibility Principle)
是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。

开放封闭原则OCP(Open-Close Principle) 
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,
那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。

里氏替换原则(the Liskov Substitution Principle LSP) 
子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,
也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。

依赖倒置原则(the Dependency Inversion Principle DIP) 具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,
这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:这样就达到
了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。一个常见的问题就是编译A模块时需要直接包含到B模块的cpp文件,而编译B时同样要直接包含到A的cpp文件。

接口分离原则(the Interface Segregation Principle ISP) 
模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来


迪米特法则
迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
英文简写为: LoD.迪米特法则可以简单说成:talk only to your immediate friends。 对于面向OOD来说,又被解释为下面几种方式:一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

  迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
迪米特法则不希望类直接建立直接的接触。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。
有兴趣可以研究一下设计模式的门面模式(Facade)和中介模式(Mediator),都是迪米特法则应用的例子。

设计模式有23种之多,但是我们经常使用的也就是那么几种,下面就和大家介绍常用的这几种设计模式:“单例模式”,“观察者模式”,“迭代器模式”,“访问者模式”。
 
一、单例模式
 
概念很简单,保证一个类仅有一个实例,并提供一个访问它的全局访问点。我就提供两段代码就好了,在游戏当中,有两种类,一种是不继承MonoBehavior,另外一种是继承它的,首先看不继承的,
Class Singleton
{
      Static MySingleton;                           // 单件对象,全局唯一的。
     Static Instance()
{
if(MySingleton == null)
             MySingleton = new MySingleton();
return MySingleton;
}       // 对外暴露接口
}
 
下面来看继承自MonoBehavior的类,
Class Singleton:MonoBehavior
{
      Static MySingleton;                          
     Static Instance()
{
return MySingleton;
}    
void Awake()
{
MySingleton = this;
}
}
直接在游戏开发中这么使用就可以了。
 
二、观察者模式
 
概念:它将对象与对象之间创建一种依赖关系,当其中一个对象发生变化时,它会将这个变化通知给与其创建关系的对象中,实现自动化的通知更新。
 
在游戏开发中,比如UI上有一个下拉的List,我选择了其中的每一项,都会导致UI界面的变化,比如我选择“强化”,对应出现强化装备的界面;我选择“镶嵌”,就会出现镶嵌的界面。
 
伪代码如下:
Class Subject
{
      // 对本目标绑定一个观察者 Attach( Observer );
      // 解除一个观察者的绑定   DeleteAttach( Observer );
      // 本目标发生改变了,通知所有的观察者,但没有传递改动了什么
      Notity()
      {
             For ( …遍历整个ObserverList …)
             { pObserver ->Update(); }
      }
      // 对观察者暴露的接口,让观察者可获得本类有什么变动       GetState();
}
// 观察者/监听者类
Class Observer
{
      // 暴露给对象目标类的函数,当监听的对象发生了变动,则它会调用本函数通知观察者
      Void Update ()
      {
            pSubject ->GetState();  
// 获取监听对象发生了什么变化
            TODO:DisposeFun();  
// 根据状态不同,给予不同的处理
      }
}
这个很好理解了吧!
 
三、迭代器模式
 
我们就拿C#中的迭代器为例直接说明即可,既能了解了迭代器模式的概念,有了解了C#中迭代器是如何实现的。
 
迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式。简单来说,迭代器模式使得你能够获取到序列中的所有元素而不用关心是其类型是array,list,linked list或者是其他什么序列结构。这一点使得能够非常高效的构建数据处理通道(data pipeline)--即数据能够进入处理通道,进行一系列的变换,或者过滤,然后得到结果。事实上,这正是LINQ的核心模式。
 
在。NET中,迭代器模式被IEnumerator和IEnumerable及其对应的泛型接口所封装。如果一个类实现了IEnumerable接口,那么就能够被迭代;调用GetEnumerator方法将返回IEnumerator接口的实现,它就是迭代器本身。迭代器类似数据库中的游标,他是数据序列中的一个位置记录。迭代器只能向前移动,同一数据序列中可以有多个迭代器同时对数据进行操作。
 
在C#1中已经内建了对迭代器的支持,那就是foreach语句。使得能够进行比for循环语句更直接和简单的对集合的迭代,编译器会将foreach编译来调用GetEnumerator和MoveNext方法以及Current属性,如果对象实现了IDisposable接口,在迭代完成之后会释放迭代器。但是在C#1中,实现一个迭代器是相对来说有点繁琐的操作。C#2使得这一工作变得大为简单,节省了实现迭代器的不少工作。
public System.Collections.IEnumerator GetEnumerator()
{    for (int i = 0; i < 10; i++)
    {        yield return i;
    }
}
static void Main()
{
    ListClass listClass1 = new ListClass();    foreach (int i in listClass1)
    {
        System.Console.Write(i + " ");
    }    // Output: 0 1 2 3 4 5 6 7 8 9}
}
 
四、访问者模式
 
当我们希望对一个结构对象添加一个功能时,我们能够在不影响结构的前提下,定义一个新的对其元素的操作。
 
例如场景管理器中管理的场景节点,是非常繁多的,而且种类不一,例如有Ogre中的Root, Irrchit中就把摄象机,灯光,Mesh,公告版,声音都做为一种场景节点,每个节点类型是不同的,虽然大家都有共通的Paint(),Hide()等方法,但方法的实现形式是不同的,当我们外界调用时需要统一接口,那么我们很可能需要需要这样的代码
       Hide( Object )
       { 
if (Object == Mesh) HideMesh(); 
if (Object == Light) HideLight();  
… 
}
 
此时若我们需要增加一个Object新的类型对象,我们就不得不对该函数进行修正。而我们可以这样做,让Mesh,Light他们都继承于Object,他们都实现一个函数Hide(),那么就变成
Mesh::Hide( Visitor ) { Visitor.Hide (Mesh); }
Light::Hide(Visitor ){ Visitor.Hide (Light); }
 
意思就是说,Mesh的隐藏可能涉及到3个步骤,Light的隐藏可能涉及到10个步骤,这样就可以在每个自己的Visitor中去实现每个元素的隐藏功能,这样就把很多跟元素类本身没用的代码转移到了Visitor中去了。
 
每个元素类可以对应于一个或者多个Visitor类。比如我们去银行柜台办业务,一般情况下会开几个个人业务柜台的,你去其中任何一个柜台办理都是可以的。我们的访问者模式可以很好付诸在这个场景中:对于银行柜台来说,他们是不用变化的,就是说今天和明天提供个人业务的柜台是不需要有变化的。而我们作为访问者,今天来银行可能是取消费流水,明天来银行可能是去办理手机银行业务,这些是我们访问者的操作,一直是在变化的。
 
伪代码如下:
 //  访问者基类
Class Visitor
{
      Virtual VisitElement( A ){ … };            
// 访问的每个对象都要写这样一个方法
      Virtual VisitElement( B ){ … };
}
// 访问者实例A
Class VisitorA
{
      VisitElement( A ){ … };       
 // 实际的处理函数
      VisitElement( B ){ … };       
 // 实际的处理函数
}
// 访问者实例B
Class VisitorB
{
      VisitElement( A ){ … };        
// 实际的处理函数
      VisitElement( B ){ … };        
// 实际的处理函数
}
// 被访问者基类
Class Element
{
      Virtual Accept( Visitor );     
// 接受访问者
}
// 被访问者实例A
Class ElementA
{
      Accecpt( Visitor v ){ v-> VisitElement(this); };    
// 调用注册到访问者中的处理函数
}
// 被访问者实例B
Class ElementB
{
      Accecpt( Visitor v ){ v-> VisitElement(this); };   
 // 调用注册到访问者中的处理函数
} 
原文地址:https://www.cnblogs.com/LiTZen/p/11444259.html