1.1 加班,又是加班!
公司的项目很急,所以要求加班。经理把每个人每天的工作都排得满满的,说做完就可以回家,但是没有任何一个人可以在下班前完成的,基本都得加班,这就等于是自愿加班。我走时还有哥们在加班呢。"
再急也不能这样呀,长时间加班,没有加班费,士气低落,效率大打折扣。
"可不是咋地!上午刚上班的时候,效率很高,可以写不少代码,到了中午,午饭一吃完,就犯困,可能是最近太累了,但还不敢休息,因为没有人趴着睡觉的,都说项目急,要抓紧。所以我就这么迷迷糊糊的,到了下午三点多才略微精神点,本想着今天任务还算可以,希望能早点完成,争取不要再加班了。哪知快下班时才发现有一个功能是我理解有误,其实比想象的要复杂得多。唉!苦呀,又多花了三个多钟头,九点多才从公司出来。"
"哈,那你自己也有问题,对工作量的判断有偏差。在公司还可以通过加班来补偿,要是在高考考场上,哪可能加时间,做不完直接就是玩完。"
"你说这老板对加班是如何想的呢?难道真的认为加班可以解决问题?我感觉这样赶进度,对代码质量没任何好处。"
"老板的想法当然是和员工不一样了。员工加班,实际上分为几种,第一种,极有可能是员工为了下班能多上会网,聊聊天,打打游戏,或者是为了学习点新东西,所以这其实根本就不能算是加班,只能算下班时坐在办公座位上。第二种,可能这个员工能力相对差,技术或业务能力不过关,或者动作慢,效率低,那当然应该要加班,而且老板也不会打算给这种菜鸟补偿。"
"我又没说是指你,除非你真的觉得自己能力差、效率低,是菜鸟。"
"不过也不得不承认,我现在经验不足确实在效率上是会受些影响的,公司里的一些骨灰级程序员,也不觉得水平特别厉害,但是总是能在下班前后就完成当天任务,而且错误很少。"
"慢慢来吧,编程水平也不是几天就可以升上去的。虽然今天你很累了,但是通过加班这件事,你也可以学到设计模式。"
"哦,听到设计模式,我就不感觉累了。来,说说看。"
"你刚才曾讲到,上午状态好,中午想睡觉,下午渐恢复,加班苦煎熬。其实是一种状态的变化,不同的时间,会有不同的状态。你现在用代码来实现一下。"
"其实就是根据时间的不同,做出判断来实现,是吧?这不是大问题。"
1.2 工作状态--函数版
package code.chapter16.state1;
public class Test {
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){
System.out.println("**********************************************");
System.out.println("《大话设计模式》代码样例");
System.out.println();
hour = 9;
writeProgram();
hour = 10;
writeProgram();
hour = 12;
writeProgram();
hour = 13;
writeProgram();
hour = 14;
writeProgram();
hour = 17;
//workFinished = true; //任务完成,下班
workFinished = false; //任务未完成,继续加班
writeProgram();
hour = 19;
writeProgram();
hour = 22;
writeProgram();
System.out.println();
System.out.println("**********************************************");
}
}
要有面向对象的思维,至少应该有个工作类
1.3 工作状态--分类版
package code.chapter16.state2;
public class Test {
public static void main(String[] args){
System.out.println("**********************************************");
System.out.println("《大话设计模式》代码样例");
System.out.println();
//紧急项目
Work emergencyProjects = new Work();
emergencyProjects.setHour(9);
emergencyProjects.writeProgram();
emergencyProjects.setHour(10);
emergencyProjects.writeProgram();
emergencyProjects.setHour(12);
emergencyProjects.writeProgram();
emergencyProjects.setHour(13);
emergencyProjects.writeProgram();
emergencyProjects.setHour(14);
emergencyProjects.writeProgram();
emergencyProjects.setHour(17);
emergencyProjects.setWorkFinished(false);
//emergencyProjects.setWorkFinished(true);
emergencyProjects.writeProgram();
emergencyProjects.setHour(19);
emergencyProjects.writeProgram();
emergencyProjects.setHour(22);
emergencyProjects.writeProgram();
System.out.println();
System.out.println("**********************************************");
}
}
//工作类
class Work{
//时间钟点
private int hour;
public int getHour(){
return this.hour;
}
public void setHour(int value){
this.hour = value;
}
//是否完成工作任务
private boolean workFinished = false;
public boolean getWorkFinished(){
return this.workFinished;
}
public void setWorkFinished(boolean value){
this.workFinished = value;
}
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 (workFinished)
System.out.println("当前时间:"+hour+"点 下班回家了");
else {
if (hour < 21)
System.out.println("当前时间:"+hour+"点 加班哦,疲累之极");
else
System.out.println("当前时间:"+hour+"点 不行了,睡着了。");
}
}
}
}
1.4 方法过长时坏味道
"仔细看看,MartinFowler曾在《重构》中写过一个很重要的代码坏味道,叫作'Long Method',方法如果过长其实极有可能是有坏味道了。"
"你的意思是'Work(工作)'类的'writeProgram(写程序)'方法过长了?不过这里面太多的判断,好像是不太好。但我也想不出来有什么办法解决它。"
"你要知道,你这个方法很长,而且有很多的判断分支,这也就意味着它的责任过大了。无论是任何状态,都需要通过它来改变,这实际上是很糟糕的。"
"哦,对的,面向对象设计其实就是希望做到代码的责任分解。这个类违背了'单一职责原则'。但如何做呢?"
"说得不错,由于'writeProgram(写程序)'的方法里有这么多判断,使得任何需求的改动或增加,都需要去更改这个方法了,比如,你们老板也感觉加班有些过分,对于公司的办公室管理以及员工的安全都不利,于是发了一个通知,不管任务再多,员工必须在20点之前离开公司。这样的需求很合常理,所以要满足需求你就得更改这个方法,但真正要更改的地方只涉及17~22点的状态,但目前的代码却是对整个方法做改动,维护出错的风险很大。"
"你解释了这么多,我的理解其实就是这样写方法违背了'开放-封闭原则'。"
那么我们应该如何做?
"把这些分支想办法变成一个又一个的类,增加时不会影响其他类。然后状态的变化在各自的类中完成。"理论讲讲很容易,但实际如何做,我想不出来。"
"当然,这需要丰富的经验积累,但实际上你是用不着再去重复发明'轮子'了,因为GoF已经为我们针对这类问题提供了解决方案,那就是'状态模式'。"
1.5 状态模式
状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。[DP]
"状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用'状态模式'了。"
状态模式(State)结构图
package code.chapter16.state0;
public class Test {
public static void main(String[] args){
System.out.println("**********************************************");
System.out.println("《大话设计模式》代码样例");
System.out.println();
Context c = new Context(new ConcreteStateA());
c.request();
c.request();
c.request();
c.request();
System.out.println();
System.out.println("**********************************************");
}
}
//抽象状态类
abstract class State {
public abstract void handle(Context context);
}
//具体状态类A
class ConcreteStateA extends State
{
public void handle(Context context) {
context.setState(new ConcreteStateB());
}
}
//具体状态类B
class ConcreteStateB extends State
{
public void handle(Context context) {
context.setState(new ConcreteStateA());
}
}
//上下文
class Context {
private State state;
public Context(State state)
{
this.state = state;
}
//可读写的状态属性,用于读取当前状态和设置新状态
public State getState(){
return this.state;
}
public void setState(State value){
this.state = value;
System.out.println("当前状态:" + this.state.getClass().getName());
}
public void request()
{
this.state.handle(this);
}
}
State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。
ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。
Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。
1.6 状态模式的好处与坏处
"状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来[DP]。"
"是不是就是将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换[DP]。"
"说白了,这样做的目的就是为了消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,就像我们最早说的刻版印刷一样,任何改动和变化都是致命的。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖,好比把整个版面改成了一个又一个的活字,此时就容易维护和扩展了。"
"什么时候应该考虑使用状态模式呢?"
"当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。另外,如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是依靠大量的多分支判断语句来实现,此时应该考虑将每一种业务状态定义为一个State的子类。于是这些对象就可以不依赖于其他对象而独立变化了,某一天客户需要更改需求,增加或减少业务状态或改变状态流程,对你来说都是不困难的事。"
"哦,明白了,这种需求还是非常常见的。"
"现在再回过头来看你的代码,那个'Long Method'你现在会改了吗?"
1.7 工作状态--状态模式版
代码结构图
package code.chapter16.state3;
public class Test {
public static void main(String[] args){
System.out.println("**********************************************");
System.out.println("《大话设计模式》代码样例");
System.out.println();
//紧急项目
Work emergencyProjects = new Work();
emergencyProjects.setHour(9);
emergencyProjects.writeProgram();
emergencyProjects.setHour(10);
emergencyProjects.writeProgram();
emergencyProjects.setHour(12);
emergencyProjects.writeProgram();
emergencyProjects.setHour(13);
emergencyProjects.writeProgram();
emergencyProjects.setHour(14);
emergencyProjects.writeProgram();
emergencyProjects.setHour(17);
emergencyProjects.setWorkFinished(false);
//emergencyProjects.setWorkFinished(true);
emergencyProjects.writeProgram();
emergencyProjects.setHour(19);
emergencyProjects.writeProgram();
emergencyProjects.setHour(22);
emergencyProjects.writeProgram();
System.out.println();
System.out.println("**********************************************");
}
}
//抽象状态类
abstract class State {
public abstract void writeProgram(Work w);
}
//上午工作状态
class ForenoonState extends State {
public void writeProgram (Work w) {
if (w.getHour() < 12) {
System.out.println("当前时间:"+ w.getHour() +"点 上午工作,精神百倍");
}
else {
w.setState(new NoonState());
w.writeProgram();
}
}
}
//中午工作状态
class NoonState extends State {
public void writeProgram (Work w) {
if (w.getHour() < 13) {
System.out.println("当前时间:"+ w.getHour() +"点 饿了,午饭;犯困,午休。");
}
else {
w.setState(new AfternoonState());
w.writeProgram();
}
}
}
//下午工作状态
class AfternoonState extends State {
public void writeProgram (Work w) {
if (w.getHour() < 17) {
System.out.println("当前时间:"+ w.getHour() +"点 下午状态还不错,继续努力");
}
else {
w.setState(new EveningState());
w.writeProgram();
}
}
}
//晚间工作状态
class EveningState extends State {
public void writeProgram(Work w)
{
if (w.getWorkFinished())
{
w.setState(new RestState());
w.writeProgram();
}
else
{
if (w.getHour() < 21) {
System.out.println("当前时间:"+ w.getHour() +"点 加班哦,疲累之极");
}
else {
w.setState(new SleepingState());
w.writeProgram();
}
}
}
}
//睡眠状态
class SleepingState extends State {
public void writeProgram(Work w) {
System.out.println("当前时间:"+ w.getHour() +"点 不行了,睡着了。");
}
}
//下班休息状态
class RestState extends State {
public void writeProgram(Work w) {
System.out.println("当前时间:"+ w.getHour() +"点 下班回家了");
}
}
//工作类
class Work {
private State current;
public Work(){
current = new ForenoonState();
}
//设置状态
public void setState(State value) {
this.current = value;
}
//写代码的状态
public void writeProgram() {
this.current.writeProgram(this);
}
//当前的钟点
private int hour;
public int getHour(){
return this.hour;
}
public void setHour(int value){
this.hour = value;
}
//当前工作是否完成
private boolean workFinished = false;
public boolean getWorkFinished(){
return this.workFinished;
}
public void setWorkFinished(boolean value){
this.workFinished = value;
}
}
抽象状态类,定义一个抽象方法"写程序"。
上午和中午工作状态类:
下午和晚间工作状态类:
睡眠状态和下班休息状态类:
工作类,此时没有了过长的分支判断语句。
客户端代码,没有任何改动。但我们的程序却更加灵活易变了。
"此时的代码,如果要完成我所说的'员工必须在20点之前离开公司',我们只需要怎么样?"
"增加一个'强制下班状态',并改动一下'晚间工作状态'类的判断就可以了。而这是不影响其他状态的代码的。这样做的确是非常好。"
"学会了状态模式,我的状态好着呢,让我再体会体会状态模式的美妙。"
"行了吧你,估计明上午的工作状态,就是睡觉打呼噜了。"
"唉,这也是公司造成的呀。明天估计还得加班,无尽加班何时休,却道天凉好个秋!"