“面向对象编程”是对现实世界的抽象,是通过代码构建出一个虚拟的世界。在虚拟世界中有人、有物,这些人和物都被称为“对象”。虚拟世界中的对象与现实世界中的一样,人有姓名、性别、年龄等一系列属性,也有吃饭、如厕、睡眠等一系列行为。
以下我通过一些真实世界中的事例,来介绍如何使用面向对象的思想来构建一个虚拟世界中的行为,并且通过“策略模式”来实现这一行为。
如上图所示,这是我们在现实世界中经常发生的行为,男士入男厕、女士入女厕。那么我们在“面向对象”的虚拟世界中如何实现这一行为呢?
首先通过代码建立“洗手间”和“人”这两个“类”
// 洗手间 // 用于声明一切洗手间的属性和行为 public interface IWashroom { // TODO: 此处省略洗手间的一些属性和行为 } // 男洗手间 // 继承“洗手间”这一接口,用于实现男洗手间独有的特性 public class MenWashroom : IWashroom { // TODO: 此处省略男洗手间的一些属性和行为 } // 女洗手间 // 继承“洗手间”这一接口,用于实现女洗手间独有的特性 public class WomenWashroom : IWashroom { // TODO: 此处省略女洗手间的一些属性和行为 }
// 人 public class Person { // 性别 public string Gender { get; set; } // 年龄 public int Age { get; set; } // 如厕 public void Goto(IWashroom washroom) { // 省略如厕的一些具体行为 } }
在以上代码构建的“人”和“洗手间”的虚拟世界中,如果一个人要如厕,那么应当和真实世界中一样,男士入男厕、女士入女厕,于是我们通过以下代码来实现这一行为:
var person = GetPerson(); //这是一个人 var menWashroom = new MenWashroom(); //这是一个男洗手间 var womenWashroom = new WomenWashroom(); //这是一个女洗手间 if(person.Gender == "男") //如果这是人的性别是男 { person.Goto(menWashroom); //那么就去男洗手间 } else if(person.Gender == "女") //如果这个人的性别是女 { person.Goto(womenWashroom); //那么就去女洗手间 }
是不是很简单?一个虚拟世界中的“厕所行为规则”就这样建立起来了。
但文明的现实世界为了方便婴幼儿和肢体残障人士,建立了“第三卫生间”
规则一下就复杂起来了,我们来梳理一下:
1.四肢健全无残疾、可独立如厕的成年男性——男厕
2.四肢健全无残疾、可独立如厕的成年女性——女厕
3.携带无法独自如厕的未成年男童、四肢健全无残疾的成年男性、男童需如厕——第三卫生间、男厕
4.携带无法独自如厕的未成年女童、四肢健全无残疾的成年男性、女童需如厕——第三卫生间
5.携带无法独自如厕的未成年男童、四肢健全无残疾的成年女性、男童需如厕——第三卫生间
6.携带无法独自如厕的未成年女童、四肢健全无残疾的成年女性、女童需如厕——第三卫生间、女厕
7.有残疾障碍可独立如厕的成年人士——第三卫生间
8.有残疾障碍但身残志坚、可独自去普通洗手间如厕的成年男性——男厕、第三卫生间
9.有残疾障碍但身残志坚、可独自去普通洗手间如厕的成年女性——女厕、第三卫生间
以上简要梳理并不涵盖所有情况,但做为本文的示例,应该可以表达出现实世界的复杂性。
考虑到现实世界如此复杂的如厕行为规则,虚拟世界若还继续采用前文所述“男士入男厕、女士入女厕”的简要行为规则的编码设计,那么至少会有9个不同的判断语句堆叠在一起,代码的可读性和可维护性将会变的极差。所以类似这种情况,我们引入设计模式中的“策略模式”设计思想,将去每一个不同洗手间想象成为不同的策略,如下:
// 人 (扩充了 携带儿童、是否残疾 两个属性) public class Person { // 姓名 public string Name { get; set; } // 性别 public string Gender { get; set; } // 年龄 public int Age { get; set; } // 携带的儿童 public Person Child { get; set; } // 是否残疾 public bool IsDisability { get; set; } }
// 第三卫生间 public class ThirdWashroom : IWashroom { // TODO: 此处省略第三卫生间的一些属性和行为 }
// 男洗手间策略 public class MenWashroomStrategy { private Person _Person; private MenWashroom _MenWashroom = GetMenWashroom(); public MenWashroomStrategy(Person man) { _Person = man; } public IWashroom GetWashroom() { // 男士 if (_Person.Gender == "男") { // 未携带儿童 if (_Person.Child == null) return _MenWashroom; // 携带男童 else if (_Person.Child.Gender == "男") return _MenWashroom; } return null; } }
// 女洗手间策略 public class WomenWashroomStrategy { private Person _Person; private WomenWashroom _WomenWashroom = GetWomenWashroom(); public WomenWashroomStrategy(Person woman) { _Person = woman; } public IWashroom GetWashroom() { // 女士 if (_Person.Gender == "女") { // 未携带儿童 if (_Person.Child == null) return _WomenWashroom; // 携带女童 else if (_Person.Child.Gender == "女") return _WomenWashroom; } return null; } }
// 第三卫生间策略 public class ThirdWashroomStrategy { private Person _Person; private ThirdWashroom _ThirdWashroom = GetThirdWashroom(); public ThirdWashroomStrategy(Person person) { _Person = person; } public IWashroom GetWashroom() { // 残疾 if (_Person.IsDisability) return _ThirdWashroom; // 携带小孩 if (_Person.Child is not null) return _ThirdWashroom; return null; } }
// 根据“人”,获取TA可以去哪种洗手间 public List<IWashroom> GetWashrooms(Person person) { var washrooms = new List<IWashroom>(); var menWashroom = new MenWashroomStrategy(person).GetWashroom(); if (menWashroom != null) washrooms.Add(menWashroom); var womenWashroom = new WomenWashroomStrategy(person).GetWashroom(); if (womenWashroom != null) washrooms.Add(womenWashroom); var thirdWashroom = new ThirdWashroomStrategy(person).GetWashroom(); if (thirdWashroom != null) washrooms.Add(thirdWashroom); return washrooms; }
如此一来,可以通过GetWashrooms函数,传入“人”,从而得到TA可以去哪种洗手间。
策略模式的优点在于:
1.避免程序中大量判断语句堆叠,造成代码的易读性降低、程序难易维护
2.每个策略类的算法都是独立的,不存在不同策略之间高度耦合,符合“高内聚、低耦合”的设计思想。
3.易适应需求变更、扩展性强,在需求少量变动的情况下,找到对应的策略类进行修改、或添加新的策略类
其缺点在于:
1.所有策略都必须是事先预知的,就像前文中表达的“男洗手间、女洗手间、第三卫生间”这些都是事先预知并固定存在的,若再冒出一个“第四卫生间”,那么需要对应用程序进行改动,新增一个策略类
2.每个策略对应一个策略类,若策略无限多则不适用于策略模式
附件为Demo(运行环境:.net 5):StategyPatternDemo.zip
好了,策略模式介绍到这里,留下几个问题供读者思考一下:
1.策略模式的主要应用场景是什么?
2.将男女洗手间、第三卫生间内部的隔间数量纳入进来,开发一个城市智慧公厕系统,你认为要考虑哪些方面?