说明
本文根据B站up主唐老狮的课程所学所记
UML
关联: 如类A会有一个类B成员作为它的成员变量
直接关联: 如母鸡类中有一个行为是下蛋,它和气候直接关联
聚合: 如地图类聚合围墙类,鸟群类聚合大雁类
依赖关系: 如动物类依赖于空气类和水类
复合: 如公司类包含各种部门类,部门类和公司类的关系就是复合关系
面向对象七大原则
总体实现目标
高内聚、低耦合,使程序模块的可重用性、移植性增强
高内聚低耦合
从类角度来看:减少类内部对其他类的调用
从功能模块来看:减少模块之间的交互复杂度
单一职责原则(SRP,Single Responsibility Principle)
类被修改的几率很大,因此应该专注于单一的功能。如果把多个功能放在同一个类中,功能之间就形成了关联,改变其中一个功能,有可能中止另一个功能。
例如:假设程序、策划、美术三个工种是三个类,他们应该各司其职,在程序世界中只应该做自己应该做的事情。
开闭原则(OCP,Open-Closed Principle)
对拓展开放,对修改关闭
拓展开放:模块的行为可以被拓展从而满足新的需求
修改关闭:不允许修改模块的源代码(或者尽量使修改最小化)
例如:继承就是最典型的开闭原则的体现,可以通过添加新的子类和重写父类的方法来实现。
里氏替换原则(LSP,Liskov Substitution Principle)
任何父类出现的地方,子类都可以替代
例如:用父类容器装载子类对象,因为子类对象包含了父类的所有内容。
依赖倒转原则(DIP,Dependence Inversion Principle)
要依赖于抽象,不要依赖于具体的实现。
例如:玩家对象抽象出开枪这一行为
迪米特原则(LoP of Demeter)
又称最少知识原则,一个对象应当对其他对象尽可能少的了解,不要和陌生人说话
例如:一个对象中的成员,要尽可能少的直接和其他类建立关系,目的是降低耦合性。
接口分离原则(ISP,Interface Segregation Principle)
不应该强迫别人依赖他们不需要使用的方法,一个接口不需要提供太多的行为,一个接口应该尽量只提供一个对外的功能,让别人去选择需要实现什么样的行为,而不是把所有的行为都封装到一个接口当中。
例如:飞行接口、走路接口、跑步接口等等虽然都是移动的行为,但是我们应该把他们分为一个一个单独的接口,让别人去选择使用。
合成复用原则(CRP,Composite Reuse Principle)
尽量使用对象组合,而不是继承来达到复用的目的,继承关系是强耦合,组合关系是低耦合。
例如:脸应该是眼镜、鼻子、嘴巴、耳朵的组合,而不是依次的继承。角色和装备也应该是组合,而不是继承。
注意:不能盲目的使用合成复用原则,要在遵循迪米特原则的前提下。
UML类图
Game对象和场景更新接口
1、创建“贪吃蛇”项目,创建“c1”文件
2、在“c1”文件下创建“Game”类
3、在“c1”文件下创建“ISceneUpdate”接口
4、对“ISceneUpdate”添加Update()方法
5、添加“Game”类所需成员变量
//窗口长宽
public int w = 80;
public int h = 20;
//当前选中场景
public ISceneUpdate nowScene;
6、构造函数实现
public Game()
{
Console.CursorVisible = false;//鼠标是否隐藏
Console.SetWindowSize(w, h);//设置窗口大小
Console.SetBufferSize(w, h);//设置缓冲区大小
}
7、实现游戏主循环:添加start函数
//实现游戏主循环:负责游戏场景逻辑的更新
public void start()
{
while(true)
{
//如果当前场景不为空就更新
if(nowScene != null)
{
nowScene.Update();
}
}
}
8、场景切换
①添加E_SceneType枚举
//场景类型枚举
enum E_SceneType
{
Begin,
Game,
End
}
②添加ChangeScene函数
//场景切换
public void ChangeScene(E_SceneType type)
{
//切场景之前应该把上一个场景的绘制内容擦掉
Console.Clear();
switch(type)
{
case E_SceneType.Begin:
break;
case E_SceneType.Game:
break;
case E_SceneType.End:
break;
}
}
修改文件展示:
Game.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.c1
{
//场景类型枚举
enum E_SceneType
{
Begin,
Game,
End
}
class Game
{
//窗口长宽
public int w = 80;
public int h = 20;
//当前选中场景
public ISceneUpdate nowScene;
public Game()
{
Console.CursorVisible = false;//鼠标是否隐藏
Console.SetWindowSize(w, h);//设置窗口大小
Console.SetBufferSize(w, h);//设置缓冲区大小
}
//实现游戏主循环:负责游戏场景逻辑的更新
public void start()
{
while(true)
{
//如果当前场景不为空就更新
if(nowScene != null)
{
nowScene.Update();
}
}
}
//场景切换
public void ChangeScene(E_SceneType type)
{
//切场景之前应该把上一个场景的绘制内容擦掉
Console.Clear();
switch(type)
{
case E_SceneType.Begin:
break;
case E_SceneType.Game:
break;
case E_SceneType.End:
break;
}
}
}
}
ISceneUpdate.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.c1
{
interface ISceneUpdate
{
void Update();
}
}
实现多场景切换
1、创建“c2”文件,添加游戏场景类GameScene,为了继承ISceneUpdate,则需要引用命名空间
using 贪吃蛇.c1;
继承接口ISceneUpdate
namespace 贪吃蛇.c2
{
class GameScene : ISceneUpdate
{
public void Update()
{
Console.SetCursorPosition(0, 0);
Console.Write("游戏场景");
}
}
}
3、添加开始和结束场景基类BeginOrEndBaseScene,并继承ISceneUpdate
using 贪吃蛇.c1;
namespace 贪吃蛇.c2
{
class BeginOrEndBaseScene : ISceneUpdate
{
public int nowSelIndex = 0;
public string strTitle;
public