简单工厂设计模式例子,工厂方法模式实例
工厂的概念在面向对象编程中反复出现,在C#本身和其他设计模式(如生成器模式)中都可以找到几个例子。在这个例子中,有一个类决定在单继承架构中实例化哪个单词类。
工厂方法模式是这一思想的巧妙扩展。superclass没有使用一个特殊的类来决定实例化哪个单词类,而是延迟了这个决定。
没有子类。这种设计模式其实是没有决策点的,也就是没有直接选择一个词类进行实例化的决策。根据这种模式编写的程序定义了一个抽象类,它创建对象,
但是让子类决定创建哪种对象。
这里,考虑一个在游泳比赛中为运动员确定泳道的相当简单的例子。在一项赛事中,游泳运动员完成若干次预赛后,按照从前一次预赛最慢到最后一次预赛最快的顺序,
对运动员成绩进行排名,下次比赛安排最快的游泳运动员在中间泳道。这种确定车道的方式称为直接排序。
目前,游泳运动员在参加锦标赛时通常游两次泳。每个游泳运动员参加预赛,前12或16名将在决赛中比赛一次。为了让预赛更公平,预赛循环排名:
最快的三名选手安排在最快的中心泳道,第二快的三名选手安排在前三组相邻的中心泳道,以此类推。
如何构建一个对象来实现这种使用分配机制,并解释工厂模式?首先,设计一个耳光抽象类事件。
1使用系统;
2使用系统。收藏;
3使用CsharpPats
四
5命名空间播种
6 {
7 ///摘要
8 ///事件的摘要描述。
9////摘要
10公共抽象类事件{
11个受保护的int numLanes
12个受保护的数组列表游泳者;
13
14公共事件(字符串文件名,int lanes) {
15 numLanes=车道;
16游泳者=new ArrayList();
17//从文件中读入游泳者
18 csFile f=新的csFile(文件名);
19 f . OpenForRead();
20 string s=f . readline();
21 while (s!=null) {
22游泳者sw=新游泳者;
23名游泳运动员。Add(软件);
24s=f . readline();
25 }
26 f . close();
27 }
28公共抽象播种get Seeding();
29公共抽象bool isPrelim();
30公共抽象bool is final();
31公共抽象bool isTimedFinal
32 }
33 }
这些抽象方法表明具体的事件类应该实现什么。接下来,有两个从Event类派生的具体类,
它们分别是PrelimEvent类和TimedFinalEvent类。这两个类的唯一区别是,一个类返回通道分配方法,另一个类返回另一个通道分配方法。我们还用以下方法定义了抽象类播种
1使用系统;
2使用系统。收藏;
3命名空间播种
4 {
5 ///摘要
6 ///种子设定的摘要描述。
7////摘要
8公共抽象类播种{
9个受保护的int numLanes
10个受保护的int[]通道;
11公共抽象IEnumerator get hunters();
12公共抽象int get count();
13 public abstract int get heats();
14受保护的抽象void seed();
15 //-
16受保护的void calcLaneOrder() {
17 lanes=new int[numLanes];
18 int mid=numLanes/2;
19 if(奇数(numLanes))
20 mid=mid 1;//从中间车道开始
21 int incr=1;
22 int ln=mid
23//创建通道阵列
24//中心到外部
25 for(int I=0;我numLanesi ) {
26车道[I]=ln;
27 ln=中间增量;
28 incr=-incr;
29 if(增量0)
30 incr=incr 1;
31 }
32 }
33 //-
34 private bool odd(int x) {
35返回((x/2)*2)!=x);
36 }
37 }
38 }
接下来,创建播种的两个特定子类:直线播种类和环形播种类。PrelimEvent类将返回CircleSeeding的实例,而TimedFinalEvent类将返回StraightSeeding的实例。
所以我们有两个架构:一个是关于事件的。
一个好像是关于播种的。
是的。
在事件继承结构中(如下图)会看到两个事件的派生类,他们含有获取种子方法一个返回直播实例,另一个返回循环播种的实例。
可以看出,它与我们前面的例子不同没有实际的工厂决策点。实例化哪一个事件类的决策就是决定实例化哪一个播种类
的决策。
在两个类继承体系结构中,尽管看起来是一对一的对应关系,但不是必须的。可以有许多这种事件类,而只有使用几种播种类。
游泳者类
除了说过的游泳者类含有名字、俱乐部、年龄、排位、时间以及选拔赛后的分组和泳道外,游泳运动员类我们并没有介绍太多事件。类从某个数据库
读入游泳者数据,然后再某项赛事中调用获取种子方法时,将数据传给播种类。
一使用系统;
2使用CsharpPats
3
四命名空间播种
5 {
6 ///摘要
7 ///游泳者的概要描述。
8////摘要
9公共级游泳运动员
10 {
11私有字符串名字,姓氏
12个私人年龄;
13私人弦乐社;
14私人浮动时间;
15
私(同Internationalorganizations)国际组织热,巷16号;
17 //-
18公共游泳者(字符串数据线路){
19 string tokenizer ST=new string tokenizer(dataline, );
20字符串行号=ST . nexttoken();//忽略并放弃
21 first name=ST . nexttoken();
22姓氏=ST . nexttoken();
23年龄=转换ToInt32 (st.nextToken().trim());
24 club=st.nextToken().trim();
25
26字符串stime=st.nextToken().trim();
27 int i=stime .(":")的索引;
28 if (i 0) {
29时间=时间。子串(0,I)时间。子串(一1);
30 }
31时间=转换100 .到单身(stime);
32
33 }
34
35 //-
36 public void setLane(int ln) {
37车道=ln
38 }
39 //-
40 public int getLane() {
41返回车道;
42 }
43 //-
44 public void setHeat(int ht) {
45热=ht
46 }
47 //-
48 public int getHeat() {
49回热;
50 }
51 //-
52 public int getAge() {
53回归年龄;
54 }
55 //-
56 public float getTime() {
57返回时间;
58 }
59 //-
60公共字符串getName() {
61返回名" "姓;
62 }
63 //-
64公共字符串getClub() {
65回归俱乐部;
66 }
67
68 }
69 }
事件类
我们在前面已经看到了抽象基类事件。在实际中,用它读入选手数据,并将其传给游泳者类的一个实例去进行分析。在抽象基类事件里,判断赛事是预赛、
决赛还是计时决赛的方法都是空的、在派生类中会实现这些方法初步事件返回循环播种的一个实例
一使用系统;
2
3命名空间播种
4 {
5 ///摘要
6///PrelimEvent的摘要说明。
7////摘要
8公共类预事件:事件
9 {
10公共预活动(字符串文件名,整数车道):base(文件名,车道){
11 }
12//返回循环播种
13公共覆盖播种getSeeding() {
14回新圈赛(游泳、跳水);
15 }
16公共覆盖bool isPrelim() {
17返回真实的
18 }
19公共覆盖bool isFinal() {
20返回假;
21 }
22公共覆盖bool istimed final(){
23返回假;
24 }
25 }
26 }
一使用系统;
2
3命名空间播种{
4 ///摘要
5///类描述将被游泳两次的事件
6////摘要
七公共类TimedFinalEvent:事件{
8
9 public timedfinalievent(string filename,int lanes):base(filename,lanes) {
10 }
11//返回直接播种类
12公共覆盖播种getSeeding() {
13名回归新直播员(游泳运动员、游泳运动员);
14 }
15公共覆盖bool isPrelim() {
16返回假;
17 }
18公共覆盖bool isFinal() {
19返回假;
20 }
21公共覆盖bool isTimedFinal
22返回真实的
23 }
24 }
25 }
直接排位
实际编写这个程序时,我们发现大多数工作都是在直接排位(直播)中完成的,为了循环排位(循环播放)而进行的改动相当少。
实例化直播类,并拷如选手和泳道号。
一个受保护的覆盖无效种子(){
2//加载swmrs数组并对其排序
3向上排序();
四
5 int lastHeat=count % numLanes
6 if (lastHeat 3)
7最后一次加热=3;//最后一次加热必须有3个或更多
8 int最后泳道=计数-最后热度;
9 numHeats=count/numLanes;
10如果(最后车道0)
11 numHeats
12 int heats=numHeats
13
14//在每个游泳者的物品中放置热量和泳道
15 int j=0;
16 for(int I=0;我喜欢小巷;i ) {
17游泳运动员SW=SW Mrs[I];
西南18度。设置lanes(lanes[j]);
19开关setHeat(加热);
20 if (j=numLanes) {
21次预赛-;
22j=0;
23 }
24 }
25//加入最后部分加热
26 if (j numLanes)
27预赛-;
28j=0;
29 for(int I=最后一条车道-1;我数;i ) {
30游泳运动员SW=SW Mrs[I];
西南31度。设置lanes(lanes[j]);
32开关setHeat(加热);
33 }
34//从数组复制回数组列表
35游泳者=new ArrayList();
36 for(int I=0;我数;我)
37名游泳运动员100 .添加(swmrs[I]);
38 }
循环排位
循环播种
类从直播类派生类中,因此一开始先调用父类的种子方法,然后重新安排赛事
一个受保护的覆盖无效种子(){
2 int圈;
3
4垒。seed();//默认使用直接种子
5 if (numHeats=2 ) {
6 if (numHeats=3)
七圆=3;
8其他
9圆=2;
10 int I=0;
11 for(int j=0;j numLanesj ) {
12 for(int k=0;k圈;k ) {
13 swmrs[i].设置lanes(lanes[j]);
14 swmrs[i ].setHeat(numHeats-k);
15 }
16 }
17 }
18 }
其他工厂
我们在前面略过了一个问题:读入运动员数据程序如何决定生成那一项赛事。再读入数据时,通过正确类型的事件来巧妙的实现这一点。
一私有void init() {
2//创建事件数组
3 events=new ArrayList();
四个事件1000个项目。添加( 500免费);
5个事件1000个项目。添加( 100免费);
6//并读入它们的数据
七个事件添加(新的timedfinaleivent( 500免费。txt ,6));
8个事件添加(新的PrelimEvent (100free.txt ,6));
9 }
很明显、这是一个需要事件工厂来决定生成哪一项赛事的例子,这里有涉及到前面的简单工厂了!
什么场景下能使用工厂模式如一下情况
1、一类无法预测他要创建的对象属于那一个类。
2、一类用它的子类来指定所创建的对象。
3、把要创建哪一类的信息局部化的时候。