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.
兵哥啊,這個事情困擾我很久了。今天抓住高手可要好好請教一下。我不是軟件科班出身,現在算是從事軟件相關工作。一則,早期自己總覺得不是本行,就沒下過大功夫,水平非常膚淺。後來痛定思痛,看了些書,打算努力好好入門,像什麽軟件工程、操作係統、設計模式之類,都大概了解。可仍然有什麽地方不對頭,總有思路和軟件格格不入,還沒有入門的感覺。
要說俺也是學工科出身,以前不管學什麽課程,隻要下功夫,沒有學不會的。唯獨這次感到軟件不同。讀了些書呐,好像也都看懂了。可是總覺得心理還是沒底,缺乏清晰的思路。就是那種沒有搔到癢處的感覺。這可是從來沒有過的。
以前都是些小的程序,還能對付。前幾年我們有一個比較大的正式一些的項目,我負責構架設計之類,那時候真是一點不懂,直接懵了。問過一些朋友,也不得要領。就是覺得一片混沌,沒有思路。
看了兵哥的 manage complexity的文章,特別有共鳴,感覺我就是缺乏在混沌中建立秩序的能力。這似乎有點是天生的。有沒有提高的可能啊?怎麽提高,兵哥給點建議。
我現在也在不斷地補課,畢竟欠缺得太多。希望有一天能厚積薄發。有哪些必須掌握的知識,兵哥給指點下吧。千萬別推辭。我們的軟件和設備打交道的多,經常用C,現在在看《C陷阱和缺陷》。沒辦法,補課了,很多東西以前真不知道。不過這些都是針對細節問題。怎麽提高全局的思路呐?這個問題已經困擾我幾年了。
讀了全篇,尤其是(1),醍醐灌頂啊。終於發現我最大的問題和弱點了。十分感謝兵哥無私地分享自己的經驗。以前,我總覺得自己和軟件開發有點格格不入,看了不少書,還總有種沒有入門的感覺。讀了兵哥的文章,才恍然大悟。我平時做事就有點缺乏條理,症結再此啊。天哪,我還有救嗎?
誠心請教兵哥,如何提高 managing complexity的能力?怎樣在混沌中建立秩序?有沒有提高的餘地?