前言
组合模式称为整体部分模式,主要是表现对象层次具备整体和部分,呈树形结构,且要求具备统一行为。
1.架构引擎时的组合模式
架构游戏引擎是离不开组合模式的,这里以Unity为例子,用组合模式简单表达一下如何架构出游戏引擎,首先我们定义出抽象组件类,组件类里应该有位置和锚点等等,具体代码如下:
public static class Global
{
public static uint index = 0;
}
public struct Size
{
public uint x;
public uint y;
}
public struct Point
{
public int x;
public int y;
}
public enum Anchor:byte
{
LeftTop,
CenterTop,
RightTop,
LeftCenter,
Center,
RightCenter,
LeftBottom,
CenterBottom,
RightBottom
}
public abstract class GameComponent
{
protected uint index;//组件编号
protected string name;//组件名字
protected Point point;//位置
protected Size size;//大小
protected Anchor anchor;//锚点
protected List<GameComponent> gameComponents;
public GameComponent(string name,Point point,Size size,Anchor anchor)
{
this.index = Global.index;
this.name = name;
this.point = point;
this.size = size;
this.anchor = anchor;
gameComponents = new List<GameComponent>();
Global.index = Global.index + 1;
}
public void ShowGameComponent()
{
}
public void HideGameComponent()
{
}
public void Show()
{
ShowGameComponent();
foreach (GameComponent gameComponent in gameComponents)
{
gameComponent.Show();
}
}
public void Hide()
{
HideGameComponent();
foreach (GameComponent gameComponent in gameComponents)
{
gameComponent.Hide();
}
}
public void AddGameComponent(GameComponent gameComponent)
{
gameComponents.Add(gameComponent);
}
public void RemoveGameComponent(uint index)
{
foreach (GameComponent gameComponent in gameComponents)
{
if (gameComponent.index == index)
{
try
{
gameComponents.Remove(gameComponent);
break;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}
public void RemoveGameComponent(GameComponent gameComponent)
{
try
{
gameComponents.Remove(gameComponent);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
public abstract class ButtonComponent:ImageComponent
{
public Action OnClick;
public Action OnHover;
public Action OnExit;
public ButtonComponent(Action OnClick, Action OnHover, Action OnExit, string
name, Point point, Size size,Anchor anchor) : base(name, point, size, anchor)
{
this.OnClick = OnClick;
this.OnHover = OnHover;
this.OnExit = OnExit;
}
}
public abstract class ImageComponent:GameComponent
{
public ImageData imageData;
public ImageComponent(ImageData imageData, string
name, Point point, Size size,Anchor anchor) : base(name, point, size, anchor)
{
this.imageData = imageData;
}
}
这里只是简单的讲解了实现原理,接下来使用框架模拟出一个界面,伪代码如下:
FormComponent root = new FormComponent(.....);
MenuComponent menu = new MenuComponent(.....);
menu.AddGameComponent(new Button(.....));
menu.AddGameComponent(new Button(.....));
menu.AddGameComponent(new Button(.....));
root.AddGameComponent(new Button(.....));
root.AddGameComponent(new Image(.....));
root.AddGameComponent(new Image(.....));
root.AddGameComponent(menu);
root.Show();
设计的时候建议把所有组件共有的部分写在GameComponent,但是按钮组件就需要另外加一些事件,所以要另外加一个ButtonComponent抽象类,这个方式在组合模式里叫做安全组合模式。有安全组合模式这个名字说明组合模式不仅仅只有一种,把所有接口都定义在GameComponent里叫透明组合模式。
2.如何实现可视化配置
可能大家看到这里,会说这个也太low了,Unity是可视化界面去创建出不同的界面,可视化配置说明代码里是没有把界面写死的,说明我们在使用Unity的时候,可能背地里生成了一个配置文件,读取这个临时的配置文件创建出不同的界面,伪代码如下:
public class NodeComponentData
{
private ComponentType componentType;
protected string name;//组件名字
protected Point point;//位置
protected Size size;//大小
protected Anchor anchor;//锚点
private List<NodeComponentData> nodeComponentDatas;
public GameComponent InitGameComponents()
{
GameComponent rootComponent = new GameComponentFactory(componentType, name, point, size, anchor);
foreach (NodeComponentData nodeComponentData in nodeComponentDatas)
{
rootComponent.AddGameComponent(nodeComponentData.InitGameComponents());
}
return rootComponent;
}
}
class Program
{
static void Main(string[] args)
{
NodeComponentData tmpData = ConfigMgr.Load();
tmpData.InitGameComponents();
}
}
其实从配置文件转换成这个NodeComponentData类时,应该把这个类也设计成完全组合的模式,这里只是简单的介绍一下大概的思想,然后我们调用InitGameComponents就可以把界面的层级关系创建出来了,InitGameComponents函数就是使用递归的思想去把组件层级表达出来的。
总结
1.透明,安全组合模式的优缺点
系统绝大多数层次具备相同的公共行为时,采用透明组合模式也许会更好(代价:为剩下少数层次节点引入不需要的方法);而如果当系统各个层次差异性行为较多或者树节点层次相对稳定(健壮)时,采用安全组合模式,所以安全组合模式符合接口最小,透明组合模式符合里氏替换原则。
2.组合模式的使用场景
1)游戏公会里面复杂的玩家职位关系。
2)游戏引擎组件之间的设计方式。