Redeployment比较适合练习和再次开发,如果你想快速学习设计模式,分层开发,模块设计,重构在真实项目中的应用,同时你的学习语言是C#,那么Redeployment比较适合你
本篇文章是我设计模式实战的第一篇,我打算把这个系列写成从应用学模式,概念由别人解释去吧,如果你想知道什么是设计模式很抱歉,我只打算写应用。呵呵
第一篇设计模式在OA中的应用
项目背景:
一个煤炭行业的OA系统,现在有个新需求:人员调动,人员调动在国有企业是常见的事情,目前我们得知需要实现3种人员调动,第一是人员岗位调动(转岗),第二人员部门调动,第三人员企业调动(国企是可以出现夸企业高层管理人员调动的),目前人员调动和部门调动企业内部可以决定,但是人员企业调动是需要上级领导部门审批。做过OA的朋友知道里面充斥的权限的问题,因此人员调动如果设计的不合理,加上权限将会给日后维护带来困难。
分析和解决:
首先找出这里的变化:调动的种类和调动信息的状态。也许还有其他的。遇到变化我们应该尝试封装,首先让我们对第一个变化做封装,看下图。
第二个变化的状态怎么封装呢?我想起自己学的状态模式(如果你还不了解状态模式请先翻阅相关资料),突然感觉是该用的时候了,查看状态模式的时候看到有人谈到状态模式和策略模式,策略模式是封装算法的变化,aha!我对第一个人员调动变化的封装不就是策略模式么?不过好像缺少一个控制对象(暂时不考虑这个)。
虽然解决了两个变化但是总感觉两者关系松散,碰到两个变化我们应该首先想到聚合不是继承,因此我觉得把第二个变化聚合到第一个内部。如图:
pedeploy腰身一变成了state的控制类,晕怎么越看越像桥接模式!!(我只能说左边的是策略模式,右边的是的状态模式,具体在一起是不是桥接模式读者有心自己去研究吧,我就不误人子弟了),而我们现在Redeploy他的控制对象呢?现在我们碰到了如何产生调动对象的问题了,ok你发现了我们应该使用工厂方法,我打算用工厂来封装调动对象实例化的变化。还是看图:
设计总结:
ok到现在整个设计完成了。总结一下首先我们因为封装变化无疑发现在使用策略模式,这也是《设计模式解析2版》说的模式可以通过原则推出来,第二模式可以组合。
那么我们来看看我们这个设计的灵活性,如果想添加新的调动情况,比如添加岗位+部门一起调动,那么我们可以在新添加一个Pedeploy的子类,然后工厂里面添加对应的语句,如果现在岗位调动需要某某检查部门审批,你现在需要做的是给state新添加子类,然后让岗位调动拥有这个新状态机制。
想看代码?ok!
代码:
工厂部分:
Code
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace Redeployment
6{
7 //工厂方法产生异动策略模式对象
8 public class RedeployFactory
9 {
10 public static RedeployStrategy GetRedeploy(string redeployKind,RedeployInfor rpinfo)
11 {
12 switch (redeployKind)
13 {
14 case "gangwei":
15 return new StationRedeployStrategy(rpinfo);
16 case "kequ":
17 return new DepartmentRedeployStrategy(rpinfo);
18 case "danwei":
19 return new CorporationRedeployStrategy(rpinfo);
20 default:
21 return null;
22 }
23 }
24 }
25
26 //异动信息
27 public class RedeployInfor
28 {
29 //身份号
30 public string ID;
31
32 //申请日期
33 public DateTime applyTime;
34
35 //申请单位
36 public string applyDepartment;
37
38 //目标值(调动的目标)
39 public string tarGet;
40
41 //备注
42 public string beiZhu;
43
44 //当前异动状态
45 public State state = null;
46 public RedeployInfor(string id, DateTime applytime, string applydepartment, string target, string beizhu, StateType stateType)
47 {
48 this.ID = id;
49 this.applyTime = applytime;
50 this.applyDepartment = applydepartment;
51 this.beiZhu = beizhu;
52 this.tarGet = target;
53
54 switch (stateType)
55 {
56 case StateType.Apply:
57 state = new ApplyState();
58 break;
59 case StateType.Wait:
60 state = new ApplyState();
61 break;
62 case StateType.Pass:
63 state = new PassState();
64 break;
65 case StateType.NoPass:
66 state = new NoPassState();
67 break;
68 default:
69 state = new ApplyState();
70 break;
71 }
72 }
73 }
74
75 //枚举状态
76 public enum StateType
77 {
78 /**//// <summary>
79 /// 申请
80 /// </summary>
81 Apply,
82
83 /**//// <summary>
84 /// 待审批
85 /// </summary>
86 Wait,
87
88 /**//// <summary>
89 /// 审批通过
90 /// </summary>
91 Pass,
92
93 /**//// <summary>
94 /// 审批未通过
95 /// </summary>
96 NoPass
97 }
98}
99
调动策略模块:
* RedeployStrategy(调动抽象类):
Code
1using System;
2using System.Collections.Generic;
3using System.Text;
4namespace Redeployment
5{
6 //策略模式管理异动的算法
7 public abstract class RedeployStrategy
8 {
9 //异动信息
10 protected RedeployInfor rpInfo;
11
12 //构造方法
13 public RedeployStrategy(RedeployInfor rpinfo)
14 {
15 rpInfo = rpinfo;
16 }
17
18 状态转换方法#region 状态转换方法
19 public void setState(State state)
20 {
21 rpInfo.state = state;
22 }
23
24 public void Pass()
25 {
26 rpInfo.state.Pass(this);
27 }
28
29 public void NoPass()
30 {
31 rpInfo.state.NoPass(this);
32 }
33 #endregion
34
35 //抽象方法异动的工作开始
36 public virtual void RedeployWorking()
37 {
38
39 }
40
41 }
42
43}
44
* StationRedeployStrategy(岗位调动类):
Code
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace Redeployment
6{
7 public class StationRedeployStrategy : RedeployStrategy
8 {
9 public StationRedeployStrategy(RedeployInfor rpinfo)
10 : base(rpinfo)
11 {
12
13 }
14 public override void RedeployWorking()
15 {
16 if (true)//点击的是Okbutton(注意okbutton意思是审批通过点击了)
17 {
18 //审批通过了
19 this.Pass();
20
21 //此地通过读取当前状态
22 string statenow = rpInfo.state.GetState();
23
24 //下面statenow配合rpInfo中的,身份号,申请日期,申请单位,备注写入数据库,目标值,和异动类型为更换岗位(注意这点)
25 //写sql
26 }
27 else
28 {
29 //审批不通过了
30 this.NoPass();
31
32 //此地通过读取当前状态
33 string statenow = this.rpInfo.state.GetState();
34
35 //下面statenow配合rpInfo中的,身份号,申请日期,申请单位,备注写入数据库,目标值,和异动类型为更换岗位(注意这点)
36 //写sql
37 }
38 }
39 }
40}
41
其他两个调度雷同如需要可下载代码。
调动状态模块:
* State(状态抽象类):
Code
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace Redeployment
6{
7 //状态模式管理流的状态
8 public abstract class State
9 {
10 public abstract void Pass(RedeployStrategy c);
11 public abstract void NoPass(RedeployStrategy c);
12 public abstract string GetState();
13 }
14}
15
* ApplyState(申请状态)
Code
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace Redeployment
6{
7 public class ApplyState : State
8 {
9 //申请通过后改变自身状态到PassState
10 public override void Pass(RedeployStrategy c)
11 {
12 c.setState(new PassState());
13 }
14
15 //申请不通过后改变自身状态到NoPassState
16 public override void NoPass(RedeployStrategy c)
17 {
18 c.setState(new NoPassState());
19 }
20
21 //获取当前状态
22 public override string GetState()
23 {
24 return "待审批";
25 }
26 }
27}
28
其他两个状态类代码请下载源码查看。
大家注意到了RedeployFactory.cs文件内定义了RedeployInfor(调动信息),这里这个类是我后来添加的,如果不添加
RedeployInfor那么调动对象的构造方法将会出现很多参数,这么重构的好处是把变化用一个类封装了,现在RedeployInfor相对稳定,调动对象的构造方法就不用因为调动信息的改变该来该去。
遗留问题:
如果调动人员很多,那么调动对象会很多,如何实现调动对象共用,我曾想过用单件模式,未果,请高手指导。
代码下载:
注:.net2.0开发
yatasoft
相关文章
Redeployment——应用