how do you manage complexity (2)

Our elevator announces the direction it's going after the floor number. It says, "8th floor, going up", or "8th floor, going down". One day, however, it simply said, "8th floor". Nothing else. I waited, nothing. Waited some more, still nothing. Until I walked away.
>You might laugh if the elevator says "8th floor, going nowhere". But it does make some sense. It eliminates uncertainty. It leaves no surprise. Most importantly, it turns an exception into a normal case.

Exceptions are a major source of complexity. Many years ago, I realized that most code are there to handle special cases. You struggle through 30 lines of code trying to figure out what a function does, only to find the answer in the last 2 lines, which calls another function. Look at this code:


private void ProcessLine(string line, IList result) {
     if (isBlank(line))
        return;
     if (isComment(line))
        return;
     string typeCode = GetTypeCode(line);
     IReaderStrategy strategy = (IReaderStrategy)_strategies[typeCode];
     if (null == strategy)
        throw new Exception("Unable to find strategy");
     result.Add(strategy.Process(line));
} 

Exceptions are also a major source of bugs. You could forget to check for zero, or get lost in a maze of if-then-else. Even if you don't, people who maintain your code will certainly do.

One obvious way to deal with exceptions is to turn them into normal cases. A good example is "8th floor, going nowhere". Another example is a Null class which simply does nothing. Experienced programmers return an empty list instead of a null, for the same reason. The above code could be simplified greatly if we have a CommentStrategy, a NullStrategy:


private void ProcessLine(string line, IList result) {
     string typeCode = GetTypeCode(line);
     IReaderStrategy strategy = (IReaderStrategy)_strategies[typeCode];
     result.Add(strategy.Process(line));
}

Many design patterns have the same goal. The Adapter pattern turns a special case into a normal case. The Composite pattern makes a group of objects to behave like a single object.

In my trading application, the user can take the same action on multiple accounts. The obvious way is to check each action to see if it's meant for multiple accounts. If so, loop through the accounts and do the action.

However, this would mean to write the same code in at least a dozen places. Using the Composite pattern, I wrote a ComboBroker class which works on multiple accounts but implements the same interface as a single account Broker. This removes the special case from client code. Moreover, the ComboBroker can implement complicated allocation logic which would be extremely messy if you have to do it in a dozen places.

Another way to deal with exceptions is to divide and conquer. In my first job, I had a chunk of Visual C++ code which draws stock charts. It was buggy. I fixed it a few times but sooner or later, a new problem would come up. I had a lot of if-then-else logic there to paint the screen in different ways for different charts and different user preferences. After a lot of frustration, I broke the chunk of code into several classes, each handling only one special case. Each class is single minded. No more if-then-else. And the bugs never came back. Incidentally, this is a Design Pattern, called Strategy.


A third way to deal with exceptions is to externalize them. I once had to fix a Unix script which calls many subscripts only available in production. I had to make many changes just to get it to run in development environment. This messed up the script. Moreover, I had to make sure to reverse all these changes before checking the code back into production, with no way to finally test it. It was very risky. The environmental exceptions not only got into my code, they also got into my process.

My solution was to create a fake production environment with fake scripts. Without touching the master script, I got it to run in development. All changes I made to it were meant for production, not for dealing with environmental exceptions. And most importantly, it went into production unchanged after a successful test run.


Exceptions are not only bad for software, they're also bad in our every day lives. One day, I left my cellphone at work. It's bad because I use it to read books on the train. I always put it on a fixed spot. However, that day, I had to charge it but the charger was on my computer. Murphy's Law says: if anything can go wrong, it will. Exceptions are evil.

It's weird for an elevator to say "8th floor, going nowhere", but for a software developer, it's brilliant. If our software is free of exception processing code, it would be tremendously simpler. Uniformity is heaven.
 

TZX999 发表评论于
了解系统整体结构后将之模块化
小马识图 发表评论于
回复 '1sthiker' 的评论 :

兵哥啊,这个事情困扰我很久了。今天抓住高手可要好好请教一下。我不是软件科班出身,现在算是从事软件相关工作。一则,早期自己总觉得不是本行,就没下过大功夫,水平非常肤浅。后来痛定思痛,看了些书,打算努力好好入门,像什么软件工程、操作系统、设计模式之类,都大概了解。可仍然有什么地方不对头,总有思路和软件格格不入,还没有入门的感觉。

要说俺也是学工科出身,以前不管学什么课程,只要下功夫,没有学不会的。唯独这次感到软件不同。读了些书呐,好像也都看懂了。可是总觉得心理还是没底,缺乏清晰的思路。就是那种没有搔到痒处的感觉。这可是从来没有过的。

以前都是些小的程序,还能对付。前几年我们有一个比较大的正式一些的项目,我负责构架设计之类,那时候真是一点不懂,直接懵了。问过一些朋友,也不得要领。就是觉得一片混沌,没有思路。

看了兵哥的 manage complexity的文章,特别有共鸣,感觉我就是缺乏在混沌中建立秩序的能力。这似乎有点是天生的。有没有提高的可能啊?怎么提高,兵哥给点建议。

我现在也在不断地补课,毕竟欠缺得太多。希望有一天能厚积薄发。有哪些必须掌握的知识,兵哥给指点下吧。千万别推辞。我们的软件和设备打交道的多,经常用C,现在在看《C陷阱和缺陷》。没办法,补课了,很多东西以前真不知道。不过这些都是针对细节问题。怎么提高全局的思路呐?这个问题已经困扰我几年了。


1sthiker 发表评论于
回复 '小马识图' 的评论 : 其实绝大多数码工都没有到 manage complexity 那个层次,大家都满足于 cut & paste 让程序工作,我本人也不例外,所以你没有必要自责:) 但是这个 manage complexity 的思路很好,能让你有鹤立鸡群的韵味,从那个高度看问题,从虚的方面说能让你有独到的见解,从实的方面说能提高你做码工的效率。
小马识图 发表评论于
去无为读了how do you manage complexity的全文。才发现原来是兵哥自己写的,还以为是老外写的哪。

读了全篇,尤其是(1),醍醐灌顶啊。终于发现我最大的问题和弱点了。十分感谢兵哥无私地分享自己的经验。以前,我总觉得自己和软件开发有点格格不入,看了不少书,还总有种没有入门的感觉。读了兵哥的文章,才恍然大悟。我平时做事就有点缺乏条理,症结再此啊。天哪,我还有救吗?

诚心请教兵哥,如何提高 managing complexity的能力?怎样在混沌中建立秩序?有没有提高的余地?
小马识图 发表评论于
读了,感觉很收益,谢谢。
Angry小鸟 发表评论于
已经读了博主全部的文章,很欣赏博主的思维逻辑和见解。期待博主多发些文章
heure 发表评论于
说声谢谢。去无为读了你几乎所有的文章,先生在专业和中英文写作方面均是超级高人。
登录后才可评论.