ls=serverList[i];
}
returnls;
}
publicvoidShowServerInfo()
{
foreach(LobbyServerlsinserverList)
{
Console.WriteLine("================="+ls.ServerName+"=================");
foreach(stringplayerinls.PlayerList)
{
Console.WriteLine(player);
}
}
}
}
classLobbyServer
{
privateListplayerList=newList();
publicListPlayerList
{
get{returnplayerList;}
}
privatestringserverName;
publicstringServerName
{
get{returnserverName;}
}
publicLobbyServer(stringserverName)
{
this.serverName=serverName;
}
publicvoidEnterPlayer(stringplayerName)
{
playerList.Add(playerName);
}
}
}
代码执行结果如下图:
代码说明
● LoadBalanceServer类实现了Singleton模式,也就是说无论在什么情况下,只会有一个LoadBalanceServer类的实例出现。
● LobbyServer类表示大厅服务,用户进入大厅后和大厅服务进行服务,在这里我们仅仅在大厅服务里面保存了用户列表。
● Singleton模式有很多实现方式,在这里使用的是双重锁定方式。
对于C#来说,可能使用静态初始化方式是最简洁的,这里就不演示了。
● LoadBalanceServer类的GetLobbyServer()方法负责返回一个压力最小的LobbyServer对象。
● 实例化LoadBalanceServer的时候Sleep了线程,目的是模拟高并发的情况,在正式代码中没有必要这样做。
何时采用
● 从代码角度来说,当你希望类只有一个实例的时候。
● 从应用角度来说,你希望有一个总管来负责某一件事情。
并且这件事情的分配只能有一个人进行,如果有多个人进行肯定会弄乱。
比如创建处理流水号如果有两个地方在创建的话是不是就会重复了呢?
实现要点
● 一个Singleton类,它能确保自身的实例是唯一的。
注意事项
● 不要滥用Singleton模式,只有非一个实例不可的情况下才考虑引入Singleton。
否则,程序的可扩展性可能会受到限制。
无废话C#设计模式之三:
AbstractFactory
意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
场景
还是上次说的那个网络游戏,定下来是一个休闲的FPS游戏。
和CS差不多,8到16个玩家在游戏里面分成2组对战射击。
现在要实现初始化场景的工作。
要呈现一个三维物体一般两个元素是少不了的,一是这个物体的骨架,也就是模型,二就是这个骨架上填充的纹理。
我们知道,这样的一个游戏不可能只有一张地图,而且地图的数量肯定是会一直增加的。
如果游戏在初始化场景的时候需要根据不同的地图分别加载模型和纹理对象,那么势必就会使得场景的扩充变得很不方便。
由此,我们引入AbstractFactory,抽象工厂生产的都是实际类型的接口(或者抽象类型),如果加了新的场景可以确保不需要修改加载场景的那部分代码。
示例代码
usingSystem;
usingSystem.Reflection;
namespaceAbstractFactoryExample
{
classProgram
{
staticvoidMain(string[]args)
{
Patrixpatrix=newPatrix();
patrix.LoadScene("HalfPaper");
patrix.LoadScene("Matrix");
}
}
classPatrix
{
privatePatrixSceneFactoryGetGameScene(stringgameSceneName)
{
return(PatrixSceneFactory)Assembly.Load("AbstractFactoryExample").CreateInstance("AbstractFactoryExample."+gameSceneName);
}
publicvoidLoadScene(stringgameSceneName)
{
PatrixSceneFactorypsf=GetGameScene(gameSceneName);
Texturetexture=psf.CreateTexture();
Modelmodel=psf.CreateModel();
model.FillTexture(texture);
}
}
abstractclassPatrixSceneFactory
{
publicabstractModelCreateModel();
publicabstractTextureCreateTexture();
}
abstractclassModel
{
publicabstractvoidFillTexture(Texturetexture);
}
abstractclassTexture
{
}
classHalfPaper:
PatrixSceneFactory
{
publicoverrideModelCreateModel()
{
returnnewHalfPaperModel();
}
publicoverrideTextureCreateTexture()
{
returnnewHalfPaperTexture();
}
}
classHalfPaperModel:
Model
{
publicHalfPaperModel()
{
Console.WriteLine("HalfPaperModelCreated");
}
publicoverridevoidFillTexture(Texturetexture)
{
Console.WriteLine("HalfPaperModelisfilledTexture");
}
}
classHalfPaperTexture:
Texture
{
publicHalfPaperTexture()
{
Console.WriteLine("HalfPaperTextureCreated");
}
}
classMatrix:
PatrixSceneFactory
{
publicoverrideModelCreateModel()
{
returnnewMatrixModel();
}
publicoverrideTextureCreateTexture()
{
returnnewMatrixTexture();
}
}
classMatrixModel:
Model
{
publicMatrixModel()
{
Console.WriteLine("MatrixModelCreated");
}
publicoverridevoidFillTexture(Texturetexture)
{
Console.WriteLine("MatrixModelisfilledTexture");
}
}
classMatrixTexture:
Texture
{
publicMatrixTexture()
{
Console.WriteLine("MatrixTextureCreated");
}
}
}
代码执行结果如下图:
代码说明
● PatrixSceneFactory就是一个抽象工厂,它声明了创建抽象的场景以及抽象的纹理的接口。
(广告时间:
Patrix是我公司的一款休闲FPS游戏,详细请见)
● Model和Texture是抽象产品。
在Model类中有一个抽象方法,用于为模型填充纹理。
● HalfPaper和Matrix是具体工厂,它用于创建某个场景的模型和纹理。
(你可能对两个类的名字不太理解,其实HalfPaper和Matrix是两个地图的名字)
● xxxModel和xxxTexture就是具体的产品了。
它们就是针对某个场景的模型和纹理,具体工厂负责创建它们。
● Patrix这个类负责加载场景,为了避免加载不同场景使用case语句,在这里我们使用反射来加载具体工厂类。
● 可以看到,一旦有了新的场景(或者说地图),我们只需要设计新的xxxModel和xxxTexture以及具体工厂类就可以了,加载场景的那部分代码(也就是Patrix类)不需要做改动。
● 我们现在这个游戏可是不支持和电脑对战的,万一以后需要支持电脑了,那么场景中的元素除了纹理和模型之外就还需要加电脑了。
也就是说抽象工厂还需要多生产一种类型的产品,这个时候抽象工厂就无能为力了。
抽象工厂只能解决系列产品扩张的变化点(在我们的例子中就是地图的新增),因此千万把抽象工厂所能生产的产品考虑周全了。
何时采用
● 从代码角度来说,你希望在统一的地方创建一系列相互关联的对象,并且基于抽象对象的时候。
● 从应用角度来说,如果你的产品是成组成套的,并且肯定会不断扩展新系列的,那么就适用抽象工厂。
比如说,外面买的塑料模型,里面总是有图纸、模型元件板和外壳包装三部分。
那么生产模型的厂就是抽象工厂,打印图纸、打印包装盒以及生产元件板的三个流水线就是具体工厂了。
需要生产新的模型,只需要制作新的图纸输入到三个流水线的电脑中就可以了。
实现要点
● 抽象工厂本身不负责创建产品,产品最终还是由具体工厂来创建的。
比如,MatrixModel是Matrix创建的,而不是PatrixSceneFactory创建的。
在.NET中可以使用反射来创建具体工厂,从而使得代码变动降到最低。
● 在抽象工厂中需要体现出生产一系列产品。
这一系列产品是相互关联,相互依赖一起使用的。
● 抽象工厂对应抽象产品,具体工厂对应具体产品,外部依赖抽象类型,这样对于新系列产品的创建,外部唯一依赖的就是具体工厂的创建过程(可以通过反射解决)。
注意事项
● 一般来说需要创建一系列对象的时候才考虑抽象工厂。
比如,创建一个场景,需要创建模型和纹理,并且模型和纹理之间是有一定联系的,不太可能把PatrixTexture套用在MatrixModel上。
● 如果系统的变化点不在新系列的扩充上,那么就没有必要使用抽象工厂。
比如,如果我们不会增加新地图的话,那么也就没有必要引入抽象工厂。
.NET中的抽象工厂
● 我们说过,抽象工厂针对系列产品的应变。
在使用ADO.NET进行数据访问的时候,