《大话设计模式》之--第16章 无尽加班何时休----状态模式

16 无尽加班何时休----状态模式

16.1加班,又是加班

“小菜,你们的加班没完没了了?”大鸟为晚上十点才到家的小菜打开了房门。

“没办法,公司的项目很急,所以要求要加班。”

“有这么急吗?这星期四天来你都在加班,有加班费吗?难道周末也要继续?”

“哪来什么加班费,周末估计是逃不了了。”小菜显然很疲惫,“经理把每个人每天的工作都排得满满的,说做完就可以回家,但是没有任何一个人可以在下班前完成的,基本都得加班,这就等于是自愿加班。我走时还有哥们在加班呢。”

“再急也不能这样呀,长时间加班,又没有加班费,士气更加低落.效率大打折扣。”

“可不是咋地!上午刚上班的时候,效率很高,可以写不少代码,到了中午,午饭一吃完,就犯困,可能是最近太累了,但还不敢休息,因为没有人趴着睡觉的,都说项目急,要抓紧。所以我就这么迷迷糊糊的,到了下午三点多才略微精神点。本想着今天任务还算可以,希望能早点完成,争取不要再加班了。哪知快下班时才发现有一个功能是我理解有误,其实比想象的要复杂得多。苦呀,又多花了三个多钟头,九点多才从公司出来。”

“哈,那你自己也有问题。对工作量的判断有偏差。在公司还可以通过加班来补偿,要是在高考考场上,哪可能加时间,做不完直接就是玩完。”

“你说这老板对加班是如何想的呢?难道真的认为加班可以解决问题?我感觉这样赶进度,对代码质量没任何好处。”

“老板的想法当然是和员工不一样了。员工加班,实际上分为几种,第一,极有可能是员工为了下班能多上会网,聊聊天,打打游戏,或者是为了学习点新东西,所以这其实根本就不能算是加班。只能算下班时坐在办公座位上,第二种,可能这个员工能力相对差,技术或业务能力不过关,或者动作慢,效率低,那当然应该要加班,而且老板也不会打算给这种菜鸟补偿。”

“大鸟,讽刺我呀。”小菜有些不满。

“我又没说是指你,除非你真的觉得自己能力差、效率低,是菜鸟。”

“不过也不得不承认,我现在经验不足确实在效率上是会受些影响的,公司里的一些骨灰级程序员,也不觉得水平特别厉害,但是总是能在下班前后就完成当天任务,而且错误很少。”

“慢慢来吧,编程水平也不是几天就可以升上去的。虽然今天你很累了。但是通过加班这件事,你也可以学到设计模式。”

“哦,听到设计模式,我就不感觉累了。来,说说看。”

“你刚才曾讲到,上午状态好,中午想睡觉,下午渐恢复,加班苦煎熬。其实是一种状态的变化,不同的时间,会有不同的状态。你现在用代码来实现一下。”

“其实就是根据时间的不同,做出判断来实现,是吧?这不是大问题。”

16.2工作状态-函数版

半小时后,小菜的第一版本程序。

 

public class Main { static int hour = 0; static Boolean workFinished = false; public static void writeProgram() { if (hour < 12) { System.out.println("当前时间:" + hour + "点 上午工作,精神百倍"); } else if (hour < 13) { System.out.println("当前时间:" + hour + "点 饿了,午饭,犯困,午休"); } else if (hour < 17) { System.out.println("当前时间:" + hour + "点 下午状态还不错,继续努力"); } else { if (workFinished) { System.out.println("当前时间:" + hour + "点 下班回家了"); } else { if (hour < 21) { System.out.println("当前时间:" + hour + "点 加班啊,疲惫至极"); } else { System.out.println("当前时间:" + hour + "点 扛不住了,睡着了"); } } } } public static void main(String[] args) { hour =9; writeProgram(); hour =10; writeProgram(); hour =12; writeProgram(); hour =13; writeProgram(); hour =14; writeProgram(); hour =17; workFinished=true; writeProgram(); hour =19; writeProgram(); hour =22; writeProgram(); } }

“小菜,都学了这么长时间的面向对象开发,你怎么还在写面向过程的代码啊?”

“我都习惯了嘛,你的意思说要分出一个类来。”

“这是起码的面向对象思维嘛,至少应该有个工作的类,你的写程序方法是类的方法,而钟点、任务完成其实就是类的什么?”

“应该就是对外属性吧。”

“问什么问,还不快去重写。”

16.3工作状态-分类版

十分钟后,小菜给出了第二版本代码。

 

//工作类 public class Work { private int hour; private boolean finish = false; public void writeProgram() { if (hour < 12) { System.out.println("当前时间:" + hour + "点 上午工作,精神百倍"); } else if (hour < 13) { System.out.println("当前时间:" + hour + "点 饿了,午饭,犯困,午休"); } else if (hour < 17) { System.out.println("当前时间:" + hour + "点 下午状态还不错,继续努力"); } else { if (finish) { System.out.println("当前时间:" + hour + "点 下班回家了"); } else { if (hour < 21) { System.out.println("当前时间:" + hour + "点 加班啊,疲惫至极"); } else { System.out.println("当前时间:" + hour + "点 扛不住了,睡着了"); } } } } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } public boolean isFinish() { return finish; } public void setFinish(boolean finish) { this.finish = finish; } } //客户端代码 public class Main { public static void main(String[] args) { Work work = new Work(); work.setHour(9); work.writeProgram(); work.setHour(10); work.writeProgram(); work.setHour(12); work.writeProgram(); work.setHour(13); work.writeProgram(); work.setHour(14); work.writeProgram(); work.setHour(17); work.setFinish(true); work.writeProgram(); work.setHour(19); work.writeProgram(); work.setHour(22); work.writeProgram(); } } 运行结果: 当前时间:9点 上午工作,精神百倍 当前时间:10点 上午工作,精神百倍 当前时间:12点 饿了,午饭,犯困,午休 当前时间:13点 下午状态还不错,继续努力 当前时间:14点 下午状态还不错,继续努力 当前时间:17点 下班回家了 当前时间:19点 下班回家了 当前时间:22点 下班回家了

16.4方法过长是Bad Smell

“若是任务完成,则17点、19点、22点都是下班回家的状态了。”

“那我现在来问问你,你觉得你写的代码有木有虾米问题?”

“木啦木啦,我觉得很好了,不然我早改了。”

“再仔细看看,MartinFowler曾在《重构》中写过一个很重要的代码Bad Smell,叫做Long Method,方法如果过长其实极有可能就是Bad Smell。”

“你的意思是Work类的writeProgram方法过长了?不过这里面判断也多嘛,好像是不太好,但我实在是想不出有什么办法可以解决它。”

“你要知道,你这个方法很长,而且有很多个判断分支,这也就意味着它的责任过大了。无论是任何状态,都需要通过它来改变,这实际上是很糟糕的。”

“啊,对的啊,面向对象设计其实就是希望做到代码的责任分解。这个类违背了‘单一职责原则’。但如何做呢?”

“说的8错,由于writeProgram的方法里有这么多判断,使得任何需求的改动或增加都需要去更改这个方法,比如,你们老板也感觉加班有些过分了,对于公司的办公室管理以及员工的安全不利,于是发了一些通知,不管任务再多,员工必须在20点之前离开公司。这样的需求很合理,所以要满足需求你就得更改这个方法,但真正要更改的地方只涉及到17点到22点之间的状态,但目前的代码却是对整个方法做改动的,维护出错的风险很大。”

“你解释了这么多,我的理解其实就是这样写方法违背了‘开放-封闭原则’。”

“哈,小菜总结的很好,对这几个原则理解的很透嘛。那么我们该如何做呢?”

“把这些分支想办法变成一个又一个类,增加时不会影响其他类。然后状态的变化在各自的类中完成。理论上讲讲很容易,但实际如何做,我想不出来。”

“当然,这需要丰富的经验积累,但实际上你是用不着再重复发明轮子了,因为GoF已经为我们针对这类问题提供了解决方案,那就是状态模式。”

16.5状态模式

状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

“状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用‘状态模式’了。”

状态模式(State)结构图

 

//State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为 public interface State { public void handle(Context context); } //ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。 public class ConcreteStateA implements State { public void handle(Context context) { context.setState(new ConcreteStateB()); } } public class ConcreteStateB implements State { public void handle(Context context) { context.setState(new ConcreteStateA()); } } //Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态 public class Context { private State state; public Context(State state) { this.state = state; } public void request() { state.handle(this); } public State getState() { return state; } public void setState(State state) { this.state = state; System.out.println("当前状态:" + state.getClass().getName()); } } //客户端代码 public class Main { public static void main(String[] args) { Context context = new Context(new ConcreteStateA()); context.request(); context.request(); context.request(); context.request(); } }

16.6状态模式好处与用处

“状态模式的好处在于将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。”

“是不是就是将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。”

“说白了,这样做的目的就是为了消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,就像我们最早说的刻版印刷一样,任何改动和变化都是致命的。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖,好比把整个版面改成了一个又一个的活字,此时就容易维护和扩展了。”

“什么时候应该考虑使用状态模式呢?”

“当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变化的行为时,就可以考虑使用状态模式了。另外如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是依靠大量的多分支判断语句来实现,此时应该考虑每一种业务状态定义为一个State的子类。这样这些对象就可以不依赖于其他对象而独立变化了,某一天客户需要更改需求,增加或减少业务状态或改变状态流程,对你来说都不是困难的事了。”

“明白了,这种需求还是非常常见的。”

“现在回头再来看你的代码,那个Long Method你现在会改了没?”

“学了状态模式,有点感觉了,我来试试看。”

16.7工作状态-状态模式版本

半个小时后,小菜给出了第三版本代码。

代码结构图

《大话设计模式》之--第16章 无尽加班何时休----状态模式_第1张图片

//State类,抽象状态类,定义一个抽象方法“写程序” public interface State { public void writeProgram(Work work); } //上午和中午工作状态类 public class ForenoonState implements State { public void writeProgram(Work work) { if (work.getHour() < 12) { System.out.println("当前时间:" + work.getHour() + "点 上午工作,精神百倍"); } else { work.setState(new NoonState()); work.writeProgram(); } } } //中午工作状态 public class NoonState implements State { public void writeProgram(Work work) { if (work.getHour() < 13) { System.out.println("当前时间:" + work.getHour() + "点 饿了,午饭,犯困,午休"); } else { work.setState(new AfternoonState()); work.writeProgram(); } } } //下午工作状态 public class AfternoonState implements State { public void writeProgram(Work work) { if (work.getHour() < 17) { System.out.println("当前时间:" + work.getHour() + "点 下午状态还不错,继续努力"); } else { work.setState(new EveningState()); work.writeProgram(); } } } //晚上工作状态 public class EveningState implements State { public void writeProgram(Work work) { if (work.isFinish()) { work.setState(new RestState()); work.writeProgram(); } else { if (work.getHour() < 21) { System.out.println("当前时间:" + work.getHour() + "点 加班啊,疲惫至极"); } else { work.setState(new SleepingState()); work.writeProgram(); } } } } //睡眠状态 public class SleepingState implements State { public void writeProgram(Work work) { System.out.println("当前时间:" + work.getHour() + "点 扛不住了,睡着了"); } } //下班休息状态 public class RestState implements State { public void writeProgram(Work work) { System.out.println("当前时间:" + work.getHour() + "点 下班回家了"); } } //工作类,此时没有了过长的分支判断语句 public class Work { private int hour; private boolean finish = false; private State state; public Work() { state = new ForenoonState(); } public void writeProgram() { state.writeProgram(this); } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } public boolean isFinish() { return finish; } public void setFinish(boolean finish) { this.finish = finish; } public State getState() { return state; } public void setState(State state) { this.state = state; } } //客户端代码,没有任何改动。但我们的程序却更加灵活易变了。 public class Main { public static void main(String[] args) { Work work = new Work(); work.setHour(9); work.writeProgram(); work.setHour(10); work.writeProgram(); work.setHour(12); work.writeProgram(); work.setHour(13); work.writeProgram(); work.setHour(14); work.writeProgram(); work.setHour(17); work.setFinish(true); work.writeProgram(); work.setHour(19); work.writeProgram(); work.setHour(22); work.writeProgram(); } } 结果显示: 当前时间:9点 上午工作,精神百倍 当前时间:10点 上午工作,精神百倍 当前时间:12点 饿了,午饭,犯困,午休 当前时间:13点 下午状态还不错,继续努力 当前时间:14点 下午状态还不错,继续努力 当前时间:17点 下班回家了 当前时间:19点 下班回家了 当前时间:22点 下班回家了

“此时的代码,如果要完成我所说的‘员工必须在20点之前离开公司’,我们只需要怎样?”

“增加一个‘强制下班状态’,并改动一下‘傍晚工作状态’类的判断就可以了。而这是不影响其他状态的代码的。这样做的确是非常好的。”

“哟,都半夜12点多了,快12点多了,快点睡觉吧。”

“学会了状态模式,我的状态好着呢,让我再体会体会状态模式的美妙。”

“行了吧你,估计明天上午的工作状态,就是睡觉打呼噜了。”

“这也是公司造成的啊。明天估计还得加班,无尽加班何时休,无尽加班何时休!”

你可能感兴趣的:(设计模式,工作,String,Class,任务,interface)