1.业务场景
生产车间中使用的条码扫描,往往一把扫描枪需要扫描不同的条码来处理不同的业务逻辑,比如,扫描投入料工位条码、扫描投入料条码、扫描产出工装条码等,每种类型的条码位数是不一样,因此通过条码长度来进行业务区分。
2.初步设计
面对此场景,能够想到的最简单的设计就是使用if...else if...或者swith进行判断,因此,我们编写的代码如下
1 switch(barCode.length) 2 { 3 case 3: 4 DoSomething1(); 5 break; 6 case 4: 7 DoSomething2(); 8 break; 9 case 5: 10 DoSomething3(); 11 break; 12 default: 13 DoSomething4(); 14 break; 15 }
使用是if...else if...或者switch已经基本上满足了需求,以后需要添加扫描场景,只需要增加判断,完成对应的方法即可。
作为一个程序员,仅仅满足需求的话,往往降低了对自己的要求,同时,随着扫描业务的增加,switch中的代码逐渐增加,多到我们自己都觉得这段代码读起来就像是吃着前天剩下的硬馒头一样,难以下咽。
3.设计提升
上述场景完全可以使用设计模式中的责任链模式来进行优化,实施步骤如下:
3.1 定义处理结果
一个处理在责任链上流动时,有两种结果,一是不能处理,转给其后继者,二是可以处理,同时又引出两种结果,处理成功和处理失败。因此,对处理结果统一定义为枚举类型
1 public enum HandleResult 2 { 3 /// <summary> 4 /// 成功 5 /// </summary> 6 Success=0, 7 /// <summary> 8 /// 失败 9 /// </summary> 10 Failed = 1, 11 /// <summary> 12 /// 未处理 13 /// </summary> 14 Unhandle = 2 15 }
3.2定义业务抽象类AbstractBarCodeHandler
定义业务抽象类,包含一个属性“Successor”,用于标记其后继者,一个公共方法“HandleRequest”,当调用真正的业务处理方法时返回未处理,则调用其后继者进行处理,一个抽象方法“HandleRequestCore”,每个扫描业务具体类,需要实现此方法,并对条码进行处理。
1 public abstract class AbstractBarCodeHandler 2 { 3 /// <summary> 4 /// 责任处理的后继者 5 /// </summary> 6 private AbstractBarCodeHandler mSuccessor; 7 8 /// <summary> 9 /// 责任处理的后继者 10 /// </summary> 11 public AbstractBarCodeHandler Successor 12 { 13 get 14 { 15 return mSuccessor; 16 } 17 18 set 19 { 20 mSuccessor = value; 21 } 22 } 23 24 /// <summary> 25 /// 请求处理方法 26 /// </summary> 27 public HandleResult HandleRequest(string barCode) 28 { 29 var result = HandleRequestCore(barCode); 30 if (result == HandleResult.Unhandle) 31 { 32 if (Successor != null) 33 { 34 Successor.HandleRequest(barCode); 35 } 36 else 37 { 38 Console.WriteLine($"The BarCode:{barCode} do not handle."); 39 } 40 } 41 return result; 42 } 43 44 protected abstract HandleResult HandleRequestCore(string barCode); 45 }
3.3定义业务实现具体类
每个具体的条码扫描业务,都定义一个类,继承自AbstractBarCodeHandler,并实现其抽象方法,比如,
LocationBarCodeHandler:工位条码处理类
LocationInBarCodeHandler:入库条码处理类
LocationOutBarCodeHandler:出库条码处理类
MaterialBarCodeHandler:物料条码处理类
ToolingBarCodeHandler:工装条码处理类
3.4 生成责任链
依次对每个类进行初始化,并设置其后继者,比如:
工位条码处理类实例.Successor=入库条码处理类实例;
入库条码处理类实例.Successor=出库条码处理类实例;
出库条码处理类实例.Successor=物料条码处理类实例;
物料条码处理类实例.Successor=工装条码处理类实例;
3.5处理过程
当条码枪扫描一个条码,便发起了一个处理请求,该请求在责任链上依次流动(工位->入库->出库->物料->工装),若某个节点不能够处理,则需要交接去后继者,若能够处理,则返回处理结果,当所有的节点都无法处理,需要给出对应的提示。
----
ServiceStack.Redis订阅发布服务的调用
1.Redis订阅发布介绍
Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息。类似于设计模式中的观察者模式。
发布者和订阅者之间使用频道进行通信,当需要发送消息时,发布者通过publish命令将消息发送到频道上,该消息就会发送给订阅这个频道的订阅者。
图片来自于http://www.runoob.com/redis/redis-pub-sub.html
2.ServiceStack.Redis
ServiceStack.Redis是Redis的C#版本的客户端,是ServiceStack的一部分。
ServiceStack的地址为https://servicestack.net
3.订阅者
首先创建RedisClient,然后调用CreateSubscription()方法创建订阅客户端,然后设置订阅客户端的几个事件:
OnMessage:接受到消息时。
OnSubscribe:订阅频道时。
OnUnSubscribe:取消订阅频道时。
最后,调用subscribeToChannels(channelName),订阅频道。
详细代码如下:
1 /// <summary> 2 /// Redis订阅 3 /// </summary> 4 public static void Subscribe() 5 { 6 using (RedisClient consumer = new RedisClient("127.0.0.1", 6379)) 7 { 8 //创建订阅 9 IRedisSubscription subscription = consumer.CreateSubscription(); 10 //接受到消息时 11 subscription.OnMessage = (channel, msg) => 12 { 13 Console.WriteLine($"从频道:{channel}上接受到消息:{msg},时间:{DateTime.Now.ToString("yyyyMMdd HH:mm:ss")}"); 14 Console.WriteLine($"频道订阅数目:{subscription.SubscriptionCount}"); 15 Console.WriteLine("___________________________________________________________________"); 16 }; 17 //订阅频道时 18 subscription.OnSubscribe = (channel) => 19 { 20 Console.WriteLine("订阅客户端:开始订阅" + channel); 21 }; 22 //取消订阅频道时 23 subscription.OnUnSubscribe = (a) => { Console.WriteLine("订阅客户端:取消订阅"); }; 24 25 //订阅频道 26 subscription.SubscribeToChannels("channel1"); 27 } 28 }
4.发布者
首先创建RedisClient,然后调用PublishMessage(channelName,message)发布消息。
详细代码如下:
RedisClient client = new RedisClient("127.0.0.1", 6379);
string message = "发布消息测试";
client.PublishMessage("channel1", message);
到目前为止,一个简单的Redis发布订阅就完成了。
5.Redis发布服务
使用发布者仅仅能够发布消息,但是不能够检测一些事件的变化,Redis中还有一个RedisPublishServer的类,里面包括一些事件能够使我们很好地检测服务的运行。
OnMessage:接受到消息;
OnStart:发布服务开始运行时;
OnStop:发布服务停止运行时;
OnUnSubscribe:订阅者取消订阅时;
OnError:发布出现错误时;
OnFailover:Redis服务器冗余切换时;
发布服务端初始化完成后,调用Start()方法,开始执行发布服务。
发布服务执行后,执行消息的发布client.PublishMessage时,发布服务端也能够接受到发布的消息。
详细代码如下:
1 public void Publish() 2 { 3 //PooledRedisClientManager 4 IRedisClientsManager redisClientManager = new PooledRedisClientManager("127.0.0.1:6379"); 5 //发布、订阅服务 IRedisPubSubServer 6 RedisPubSubServer pubSubServer = new RedisPubSubServer(redisClientManager, "channel1") 7 { 8 OnMessage = (channel, msg) => 9 { 10 Console.WriteLine($"从频道:{channel}上接受到消息:{msg},时间:{DateTime.Now.ToString("yyyyMMdd HH:mm:ss")}"); 11 Console.WriteLine("___________________________________________________________________"); 12 }, 13 OnStart = () => 14 { 15 Console.WriteLine("发布服务已启动"); 16 Console.WriteLine("___________________________________________________________________"); 17 }, 18 OnStop = () => { Console.WriteLine("发布服务停止"); }, 19 OnUnSubscribe = channel => { Console.WriteLine(channel); }, 20 OnError = e => { Console.WriteLine(e.Message); }, 21 OnFailover = s => { Console.WriteLine(s); }, 22 }; 23 //接收消息 24 pubSubServer.Start(); 25 }