??xml version="1.0" encoding="utf-8" standalone="yes"?>99久久精品国产一区二区三区,米奇精品一区二区三区在线观看,亚洲成av人在线观看http://www.aygfsteel.com/sealyu/category/30679.html--- The devil's in the Detailszh-cnFri, 11 Jun 2010 18:12:13 GMTFri, 11 Jun 2010 18:12:13 GMT60UML设计?U图??http://www.aygfsteel.com/sealyu/archive/2010/05/20/321428.htmlsealsealWed, 19 May 2010 17:31:00 GMThttp://www.aygfsteel.com/sealyu/archive/2010/05/20/321428.htmlhttp://www.aygfsteel.com/sealyu/comments/321428.htmlhttp://www.aygfsteel.com/sealyu/archive/2010/05/20/321428.html#Feedback0http://www.aygfsteel.com/sealyu/comments/commentRss/321428.htmlhttp://www.aygfsteel.com/sealyu/services/trackbacks/321428.html? UML不是很了解,单得了解一下UML设计中有的图例及基本作用。首先对UML中的各个囄功用做一个简单介l:
 
1、用例图
 
    说明的是谁要使用pȝQ以及他们用该pȝ可以做些什么。一个用例图包含了多个模型元素,如系l、参与者和用例Qƈ且显CZq些元素之间的各U关p,如泛 化、关联和依赖?/font>
 
2、类?/strong>
 
    能够让我们在正确~写代码以前对系l有一个全面的认识。类图是一U模型类型,切的说Q是一U静态模型类型。一个类图根据系l中的类Q以及各个类之间的关 pLq系l的静态视图?/font>
 
3、对象图
 
    与类图极为相|只是它描q的不是cM间的关系Q而是对象之间的关p?/font>
 
4、活动图
 
    能够演示出系l中哪些地方存在功能Q以及这些功能和pȝ中其他组件的功能如何共同满前面使用用例囑־模的商务需求?/font>
 
5、状态图
 
    可以捕获对象、子pȝ和系l的生命周期。他们可以告知一个对象可以拥有的状态,q且事g(如小溪的接收、时间的逝、错误、条件变为真{?会怎么随着旉 的推UL影响q些状态。一个状态图应该q接到所有具有清晰的可标识状态和复杂行ؓ的类Q该囑֏以确定类的行为,以及该行为如何根据当前的状态变化,也可? 展示哪些事g会改变cȝ对象的状态?/font>
 
6、顺序图
 
    是用来显CZ的参与者如何以一pd序的步骤与pȝ的对象交互的模型。顺序图可以用来展示对象之间是如何进行交互的。顺序图显C的重点攑֜消息序列上, x息是如何在对象之间被发送和接收的?/font>
 
7、协作图
 
    可以看成是类囑֒序囄交集Q协作图建模对象或者角Ԍ以及它们彼此之间是如何通信的?/font>
 
8、构件图
 
    用来建模软g的组件及其相互之间的关系Q这些图由构件标记符和构件之间的关系构成。在lg图中Q构件时软g单个l成部分Q它可以是一个文Ӟ产品、可执行 文g和脚本等?/font>
 
9、部|图
 
    是用来徏模系l的物理部v。例如计机和设备,以及它们之间是如何连接的。部|图的用者是开发h员、系l集成h员和试人员?/font>
 
 
-------------------------------------------------------------------------------------------------------------
 
    然后转一blog对各个图做一个比较详l的说明和D例:
 
**********************************************************
UML(2009-04-11 12:18:57)
标签Quml 状态图 cd it   分类QY件设?
 
**********************************************************
 
UML(l一建模语言)Q是面向对象的可视化建模的一U语a。是数据? 设计q程中,在E-R图(实体-联系图)的设计后的进一步徏模?br /> UML中有3U构造块Q事物、关pd图,事物是对模型中最h代表性的成分的抽 象;关系是把事物l合在一P图聚集了相关的的事物。具体关pd标如下:
 
uml01.jpg

说明Q?br /> 构g事物是名词,是模型的静态部分?br /> 行ؓ事物是动态部分,表示行ؓ?br /> ? l事物是l织部分?br /> 注释事物是解释部分?/font>
 
依赖Q一个事物变化会引v另一个事物变化?br /> 聚集Q特D的? 联,描述整体与部分的l合关系?br /> 泛化Q是一U特D与一般的关系Q如子元素(ҎQ与父元素(一般)Q箭头指向父元素?br /> 实现Q类元之间的? p,其中一个类元指定了由另一个类元保证执行的契约。一般用在接口和实现他们的类之间或用例和实现它们的协作之间?/font>
 
UML提供9U视图:cd、对象图Q用例图Q序列图、协作图Q状? 图、活动图Q构件图和部|图?/font>

在UMLpȝ开发中有三个主要的模型Q?br />
功能模型: 从用L角度展示pȝ的功能,包括用例图?br /> 对象 模型: 采用对象Q属性,操作Q关联等概念展示pȝ的结构和基础Q包括类图?br /> 动态模? 展现pȝ的内部行为?包括序列图,zd图,状态图?/font>
 

下面具体说明Q?/font>

1.cdQ描qCl对象、接口、协作等事物之间的关pR如下图(摘自|络)Q?/font>
 
uml02.jpg
 
 注:#表示protectedQ?表示PublicQ?表示 private


2.对象?/strong>Q描qCl对象之间的关系Q是h具体属性值和行ؓ的一个具体事物,其是cd中所Z 物实例的静态快照,其与cd的主要区别是一个是抽象?而对象图是具体的。如下图(摘自|络)Q?/font>
 
uml03.jpg

3.用例?/strong>Q描qCl用例、参与者以及它们之间的关系Q其展示的是该系l在它的外面环境中所提供 的外部可见服务。如下图(摘自|络)Q?/font>
 
uml04.jpg

4.交互?/strong>Q包?strong>序列?/strong>Q?font color="#ff0000">序?/font>Q和协作?/strong>Q两者对应,序图是消息旉序Q有对象生命 U和控制焦点。协作图是强调接收和发送消息的对象的结构组l,有\径和序受如下图(摘自|络)Q?/font>
 
序列图:
 
uml05.jpg
 
协作图:
 
uml06.jpg

5.状态图Q展CZ一个状态机Q由状态、{换、事件和zdl成。强调事件行为的序。如下图(? 自网l?Q?/font>
 
uml07.jpg
 
6.zd?/strong>Q是一U特D的状态图Q实C个活动到另一个活动的程。如下图(摘自|络)Q?/font>
 
uml08.jpg
 
7.构g囑֒部v?/strong>Q构件图展示一l构件之间的l织和依赖关p,q以全局的模型展C出来。部|图 是构件的配置及描q系l如何在g上部|Ӏ如下图(摘自|络)Q?/font>
 
uml09.jpg
 
                                                               < 如有不Q请指正>
------------------------------------------------------------------------------------------
 
    Nq么多的囄的不Ҏ啊,我试了一下太ȝ放弃了Q对q个作者表C钦佩?/span>
 
    好了Q其他如果需要更具体的介l,可以看下面这个地址Q说的比较详l: http://www.cnblogs.com/macou/archive/2009/02/18/1392903.html
 
 
 




-The End-



seal 2010-05-20 01:31 发表评论
]]>
The Tao Of Programminghttp://www.aygfsteel.com/sealyu/archive/2010/05/08/320337.htmlsealsealFri, 07 May 2010 21:52:00 GMThttp://www.aygfsteel.com/sealyu/archive/2010/05/08/320337.htmlhttp://www.aygfsteel.com/sealyu/comments/320337.htmlhttp://www.aygfsteel.com/sealyu/archive/2010/05/08/320337.html#Feedback0http://www.aygfsteel.com/sealyu/comments/commentRss/320337.htmlhttp://www.aygfsteel.com/sealyu/services/trackbacks/320337.html Translated by Geoffrey James

Transcribed by Duke Hillard

Transmitted by Anupam Trivedi, Sajitha Tampi, and Meghshyam Jagannath

Re-html-ized and edited by Kragen Sittler

Last modified 1996-04-10 or earlier


Table of Contents

  1. The Silent Void
  2. The Ancient Masters
  3. Design
  4. Coding
  5. Maintenance
  6. Management
  7. Corporate Wisdom
  8. Hardware and Software
  9. Epilogue

Book 1 - The Silent Void

Thus spake the master programmer:

``When you have learned to snatch the error code from the trap frame, it will be time for you to leave.''

1.1

Something mysterious is formed, born in the silent void. Waiting alone and unmoving, it is at once still and yet in constant motion. It is the source of all programs. I do not know its name, so I will call it the Tao of Programming.

If the Tao is great, then the operating system is great. If the operating system is great, then the compiler is great. If the compiler is great, then the application is great. The user is pleased and there exists harmony in the world.

The Tao of Programming flows far away and returns on the wind of morning.

1.2

The Tao gave birth to machine language. Machine language gave birth to the assembler.

The assembler gave birth to the compiler. Now there are ten thousand languages.

Each language has its purpose, however humble. Each language expresses the Yin and Yang of software. Each language has its place within the Tao.

But do not program in COBOL if you can avoid it.

1.3

In the beginning was the Tao. The Tao gave birth to Space and Time. Therefore Space and Time are Yin and Yang of programming.

Programmers that do not comprehend the Tao are always running out of time and space for their programs. Programmers that comprehend the Tao always have enough time and space to accomplish their goals.

How could it be otherwise?

1.4

The wise programmer is told about Tao and follows it. The average programmer is told about Tao and searches for it. The foolish programmer is told about Tao and laughs at it.

If it were not for laughter, there would be no Tao.

The highest sounds are hardest to hear.
Going forward is a way to retreat.
Great talent shows itself late in life.
Even a perfect program still has bugs.


Book 2 - The Ancient Masters

Thus spake the master programmer:

``After three days without programming, life becomes meaningless.''

2.1

The programmers of old were mysterious and profound. We cannot fathom their thoughts, so all we do is describe their appearance.

Aware, like a fox crossing the water. Alert, like a general on the battlefield. Kind, like a hostess greeting her guests. Simple, like uncarved blocks of wood. Opaque, like black pools in darkened caves.

Who can tell the secrets of their hearts and minds?

The answer exists only in Tao.

2.2

Grand Master Turing once dreamed that he was a machine. When he awoke he exclaimed:

``I don't know whether I am Turing dreaming that I am a machine, or a machine dreaming that I am Turing!''

2.3

A programmer from a very large computer company went to a software conference and then returned to report to his manager, saying: ``What sort of programmers work for other companies? They behaved badly and were unconcerned with appearances. Their hair was long and unkempt and their clothes were wrinkled and old. They crashed our hospitality suite and they made rude noises during my presentation.''

The manager said: ``I should have never sent you to the conference. Those programmers live beyond the physical world. They consider life absurd, an accidental coincidence. They come and go without knowing limitations. Without a care, they live only for their programs. Why should they bother with social conventions?

``They are alive within the Tao.''

2.4

A novice asked the Master: ``Here is a programmer that never designs, documents or tests his programs. Yet all who know him consider him one of the best programmers in the world. Why is this?''

The Master replies: ``That programmer has mastered the Tao. He has gone beyond the need for design; he does not become angry when the system crashes, but accepts the universe without concern. He has gone beyond the need for documentation; he no longer cares if anyone else sees his code. He has gone beyond the need for testing; each of his programs are perfect within themselves, serene and elegant, their purpose self-evident. Truly, he has entered the mystery of Tao.''


Book 3 - Design

Thus spake the master programmer:

``When the program is being tested, it is too late to make design changes.''

3.1

There once was a man who went to a computer trade show. Each day as he entered, the man told the guard at the door:

``I am a great thief, renowned for my feats of shoplifting. Be forewarned, for this trade show shall not escape unplundered.''

This speech disturbed the guard greatly, because there were millions of dollars of computer equipment inside, so he watched the man carefully. But the man merely wandered from booth to booth, humming quietly to himself.

When the man left, the guard took him aside and searched his clothes, but nothing was to be found.

On the next day of the trade show, the man returned and chided the guard saying: ``I escaped with a vast booty yesterday, but today will be even better.'' So the guard watched him ever more closely, but to no avail.

On the final day of the trade show, the guard could restrain his curiosity no longer. ``Sir Thief,'' he said, ``I am so perplexed, I cannot live in peace. Please enlighten me. What is it that you are stealing?''

The man smiled. ``I am stealing ideas,'' he said.

3.2

There once was a master programmer who wrote unstructured programs. A novice programmer, seeking to imitate him, also began to write unstructured programs. When the novice asked the master to evaluate his progress, the master criticized him for writing unstructured programs, saying, ``What is appropriate for the master is not appropriate for the novice. You must understand the Tao before transcending structure.''

3.3

There was once a programmer who was attached to the court of the warlord of Wu. The warlord asked the programmer: ``Which is easier to design: an accounting package or an operating system?''

``An operating system,'' replied the programmer.

The warlord uttered an exclamation of disbelief. ``Surely an accounting package is trivial next to the complexity of an operating system,'' he said.

``Not so,'' said the programmer, ``when designing an accounting package, the programmer operates as a mediator between people having different ideas: how it must operate, how its reports must appear, and how it must conform to the tax laws. By contrast, an operating system is not limited by outside appearances. When designing an operating system, the programmer seeks the simplest harmony between machine and ideas. This is why an operating system is easier to design.''

The warlord of Wu nodded and smiled. ``That is all good and well, but which is easier to debug?''

The programmer made no reply.

3.4

A manager went to the master programmer and showed him the requirements document for a new application. The manager asked the master: ``How long will it take to design this system if I assign five programmers to it?''

``It will take one year,'' said the master promptly.

``But we need this system immediately or even sooner! How long will it take if I assign ten programmers to it?''

The master programmer frowned. ``In that case, it will take two years.''

``And what if I assign a hundred programmers to it?''

The master programmer shrugged. ``Then the design will never be completed,'' he said.


Book 4 - Coding

Thus spake the master programmer:

``A well-written program is its own heaven; a poorly-written program is its own hell.''

4.1

A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity.

A program should follow the `Law of Least Astonishment'. What is this law? It is simply that the program should always respond to the user in the way that astonishes him least.

A program, no matter how complex, should act as a single unit. The program should be directed by the logic within rather than by outward appearances.

If the program fails in these requirements, it will be in a state of disorder and confusion. The only way to correct this is to rewrite the program.

4.2

A novice asked the master: ``I have a program that sometime runs and sometimes aborts. I have followed the rules of programming, yet I am totally baffled. What is the reason for this?''

The master replied: ``You are confused because you do not understand Tao. Only a fool expects rational behavior from his fellow humans. Why do you expect it from a machine that humans have constructed? Computers simulate determinism; only Tao is perfect.

``The rules of programming are transitory; only Tao is eternal. Therefore you must contemplate Tao before you receive enlightenment.''

``But how will I know when I have received enlightenment?'' asked the novice.

``Your program will then run correctly,'' replied the master.

4.3

A master was explaining the nature of Tao of to one of his novices. ``The Tao is embodied in all software - regardless of how insignificant,'' said the master.

``Is the Tao in a hand-held calculator?'' asked the novice.

``It is,'' came the reply.

``Is the Tao in a video game?'' continued the novice.

``It is even in a video game,'' said the master.

``And is the Tao in the DOS for a personal computer?''

The master coughed and shifted his position slightly. ``The lesson is over for today,'' he said.

4.4

Prince Wang's programmer was coding software. His fingers danced upon the keyboard. The program compiled without an error message, and the program ran like a gentle wind.

``Excellent!'' the Prince exclaimed, ``Your technique is faultless!''

``Technique?'' said the programmer turning from his terminal, ``What I follow is Tao - beyond all techniques! When I first began to program I would see before me the whole problem in one mass. After three years I no longer saw this mass. Instead, I used subroutines. But now I see nothing. My whole being exists in a formless void. My senses are idle. My spirit, free to work without plan, follows its own instinct. In short, my program writes itself. True, sometimes there are difficult problems. I see them coming, I slow down, I watch silently. Then I change a single line of code and the difficulties vanish like puffs of idle smoke. I then compile the program. I sit still and let the joy of the work fill my being. I close my eyes for a moment and then log off.''

Prince Wang said, ``Would that all of my programmers were as wise!''


Book 5 - Maintenance

Thus spake the master programmer:

``Though a program be but three lines long, someday it will have to be maintained.''

5.1

A well-used door needs no oil on its hinges.
A swift-flowing stream does not grow stagnant.
Neither sound nor thoughts can travel through a vacuum.
Software rots if not used.

These are great mysteries.

5.2

A manager asked a programmer how long it would take him to finish the program on which he was working. ``It will be finished tomorrow,'' the programmer promptly replied.

``I think you are being unrealistic,'' said the manager, ``Truthfully, how long will it take?''

The programmer thought for a moment. ``I have some features that I wish to add. This will take at least two weeks,'' he finally said.

``Even that is too much to expect,'' insisted the manager, ``I will be satisfied if you simply tell me when the program is complete.''

The programmer agreed to this.

Several years later, the manager retired. On the way to his retirement luncheon, he discovered the programmer asleep at his terminal. He had been programming all night.

5.3

A novice programmer was once assigned to code a simple financial package.

The novice worked furiously for many days, but when his master reviewed his program, he discovered that it contained a screen editor, a set of generalized graphics routines, an artificial intelligence interface, but not the slightest mention of anything financial.

When the master asked about this, the novice became indignant. ``Don't be so impatient,'' he said, ``I'll put in the financial stuff eventually.''

5.4

Does a good farmer neglect a crop he has planted?
Does a good teacher overlook even the most humble student?
Does a good father allow a single child to starve?
Does a good programmer refuse to maintain his code?


Book 6 - Management

Thus spake the master programmer:

``Let the programmers be many and the managers few - then all will be productive.''

6.1

When managers hold endless meetings, the programmers write games. When accountants talk of quarterly profits, the development budget is about to be cut. When senior scientists talk blue sky, the clouds are about to roll in.

Truly, this is not the Tao of Programming.

When managers make commitments, game programs are ignored. When accountants make long-range plans, harmony and order are about to be restored. When senior scientists address the problems at hand, the problems will soon be solved.

Truly, this is the Tao of Programming.

6.2

Why are programmers non-productive?
Because their time is wasted in meetings.

Why are programmers rebellious?
Because the management interferes too much.

Why are the programmers resigning one by one?
Because they are burnt out.

Having worked for poor management, they no longer value their jobs.

6.3

A manager was about to be fired, but a programmer who worked for him invented a new program that became popular and sold well. As a result, the manager retained his job.

The manager tried to give the programmer a bonus, but the programmer refused it, saying, ``I wrote the program because I thought it was an interesting concept, and thus I expect no reward.''

The manager upon hearing this remarked, ``This programmer, though he holds a position of small esteem, understands well the proper duty of an employee. Let us promote him to the exalted position of management consultant!''

But when told this, the programmer once more refused, saying, ``I exist so that I can program. If I were promoted, I would do nothing but waste everyone's time. Can I go now? I have a program that I'm working on."

6.4

A manager went to his programmers and told them: ``As regards to your work hours: you are going to have to come in at nine in the morning and leave at five in the afternoon.'' At this, all of them became angry and several resigned on the spot.

So the manager said: ``All right, in that case you may set your own working hours, as long as you finish your projects on schedule.'' The programmers, now satisfied, began to come in at noon and work to the wee hours of the morning.


Book 7 - Corporate Wisdom

Thus spake the master programmer:

``You can demonstrate a program for a corporate executive, but you can't make him computer literate.''

7.1

A novice asked the master: ``In the east there is a great tree-structure that men call `Corporate Headquarters'. It is bloated out of shape with vice presidents and accountants. It issues a multitude of memos, each saying `Go, Hence!' or `Go, Hither!' and nobody knows what is meant. Every year new names are put onto the branches, but all to no avail. How can such an unnatural entity be?"

The master replied: ``You perceive this immense structure and are disturbed that it has no rational purpose. Can you not take amusement from its endless gyrations? Do you not enjoy the untroubled ease of programming beneath its sheltering branches? Why are you bothered by its uselessness?''

7.2

In the east there is a shark which is larger than all other fish. It changes into a bird whose wings are like clouds filling the sky. When this bird moves across the land, it brings a message from Corporate Headquarters. This message it drops into the midst of the programmers, like a seagull making its mark upon the beach. Then the bird mounts on the wind and, with the blue sky at its back, returns home.

The novice programmer stares in wonder at the bird, for he understands it not. The average programmer dreads the coming of the bird, for he fears its message. The master programmer continues to work at his terminal, for he does not know that the bird has come and gone.

7.3

The Magician of the Ivory Tower brought his latest invention for the master programmer to examine. The magician wheeled a large black box into the master's office while the master waited in silence.

``This is an integrated, distributed, general-purpose workstation,'' began the magician, ``ergonomically designed with a proprietary operating system, sixth generation languages, and multiple state of the art user interfaces. It took my assistants several hundred man years to construct. Is it not amazing?''

The master raised his eyebrows slightly. ``It is indeed amazing,'' he said.

``Corporate Headquarters has commanded,'' continued the magician, ``that everyone use this workstation as a platform for new programs. Do you agree to this?''

``Certainly,'' replied the master, ``I will have it transported to the data center immediately!'' And the magician returned to his tower, well pleased.

Several days later, a novice wandered into the office of the master programmer and said, ``I cannot find the listing for my new program. Do you know where it might be?''

``Yes,'' replied the master, ``the listings are stacked on the platform in the data center.''

7.4

The master programmer moves from program to program without fear. No change in management can harm him. He will not be fired, even if the project is cancelled. Why is this? He is filled with Tao.


Book 8 - Hardware and Software

Thus spake the master programmer:

``Without the wind, the grass does not move. Without software, hardware is useless.''

8.1

A novice asked the master: ``I perceive that one computer company is much larger than all others. It towers above its competition like a giant among dwarfs. Any one of its divisions could comprise an entire business. Why is this so?''

The master replied, ``Why do you ask such foolish questions? That company is large because it is large. If it only made hardware, nobody would buy it. If it only made software, nobody would use it. If it only maintained systems, people would treat it like a servant. But because it combines all of these things, people think it one of the gods! By not seeking to strive, it conquers without effort.''

8.2

A master programmer passed a novice programmer one day. The master noted the novice's preoccupation with a hand-held computer game. ``Excuse me,'' he said, ``may I examine it?''

The novice bolted to attention and handed the device to the master. ``I see that the device claims to have three levels of play: Easy, Medium, and Hard,'' said the master. ``Yet every such device has another level of play, where the device seeks not to conquer the human, nor to be conquered by the human.''

``Pray, great master,'' implored the novice, ``how does one find this mysterious setting?''

The master dropped the device to the ground and crushed it underfoot. And suddenly the novice was enlightened.

8.3

There was once a programmer who worked upon microprocessors. ``Look at how well off I am here,'' he said to a mainframe programmer who came to visit, ``I have my own operating system and file storage device. I do not have to share my resources with anyone. The software is self- consistent and easy-to-use. Why do you not quit your present job and join me here?''

The mainframe programmer then began to describe his system to his friend, saying ``The mainframe sits like an ancient sage meditating in the midst of the data center. Its disk drives lie end-to-end like a great ocean of machinery. The software is as multifaceted as a diamond, and as convoluted as a primeval jungle. The programs, each unique, move through the system like a swift-flowing river. That is why I am happy where I am.''

The microcomputer programmer, upon hearing this, fell silent. But the two programmers remained friends until the end of their days.

8.4

Hardware met Software on the road to Changtse. Software said: ``You are Yin and I am Yang. If we travel together we will become famous and earn vast sums of money.'' And so the set forth together, thinking to conquer the world.

Presently they met Firmware, who was dressed in tattered rags and hobbled along propped on a thorny stick. Firmware said to them: ``The Tao lies beyond Yin and Yang. It is silent and still as a pool of water. It does not seek fame, therefore nobody knows its presence. It does not seek fortune, for it is complete within itself. It exists beyond space and time.''

Software and Hardware, ashamed, returned to their homes.


Book 9 - Epilogue

Thus spake the master programmer:

``It is time for you to leave.''




seal 2010-05-08 05:52 发表评论
]]>
领域驱动设计和开发实??http://www.aygfsteel.com/sealyu/archive/2010/03/29/316783.htmlsealsealMon, 29 Mar 2010 00:55:00 GMThttp://www.aygfsteel.com/sealyu/archive/2010/03/29/316783.htmlhttp://www.aygfsteel.com/sealyu/comments/316783.htmlhttp://www.aygfsteel.com/sealyu/archive/2010/03/29/316783.html#Feedback0http://www.aygfsteel.com/sealyu/comments/commentRss/316783.htmlhttp://www.aygfsteel.com/sealyu/services/trackbacks/316783.html阅读全文

seal 2010-03-29 08:55 发表评论
]]>
正方形是否是长方形的子类?Q{Q?/title><link>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307045.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 23 Dec 2009 07:39:00 GMT</pubDate><guid>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307045.html</guid><wfw:comment>http://www.aygfsteel.com/sealyu/comments/307045.html</wfw:comment><comments>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307045.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/sealyu/comments/commentRss/307045.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/sealyu/services/trackbacks/307045.html</trackback:ping><description><![CDATA[<p><strong><font size="3">长方形和正方?/font></strong></p> <p><font size="3">正方形是否是长方形的子类的问题,西方一个很著名的思L题?/font></p> <p><font size="3">正确的写法是Q?/font></p> <p><font size="3">长方形类Q两个属性,宽度和高度;正方形类Q一个属性,辏V?/font></p> <p><font size="3">QLY注:q是臛_行了十q的思L题目Q最早来自于C++和Smalltalk领域。类似的q种思L问题q有哪些呢?让我不禁对哲学又感冒h了。查阅资料时意外扑ֈ了一个讨论区Q里面有读者和作者关于此处的拓展讨论Q真让h高兴。)</font></p> <p><font size="3"><font size="+0"><font size="3">(LY注:书中没有提契U即Design by Contract的概c子cd当完全承父cȝcontract?/font><font size="3">《敏 捯Y件开发:原则、模式与实践》一书中q样写,"Z契约设计(Design By Constract),UDBC"q项技术对LISKOV代换原则提供了支?该项技术Bertrand Meyer伯特兰做q详l的介绍:使用DBC,cȝ~写者显式地规定针对该类的契U?客户代码的编写者可以通过该契U获悉可以依赖的行ؓ方式.契约是通过 每个Ҏ声明的前|条?preconditions)和后|条?postconditions)来指定的.要一个方法得以执?前置条g必须为真. 执行完毕?该方法要保证后置条g为真.是?在重新声明派生类中的例程(routine)?只能使用相等或者更q前置条g来替换原始的前置条g, 只能使用相等或者更强的后置条g来替换原始的后置条g?nbsp; 本书中长方Ş的Contract是width和height可以独立变化Q这个contract在正方Ş中被破坏了?</font></font></font></p> <p><font size="3"><font size="+0"><font size="3">QLY注:注意Q我们所有讨论的基础都应q的行为决定。这使得长方形等cL动态的Q而不是象现实生活中一h静态的概念。)</font></font></font></p> <p><font size="3"><font size="+0"> </font></font></p> <p><font size="3"><font size="+0"><strong><font size="3">正方形不可以作ؓ长方形的子类</font></strong></font></font></p> <p><font size="3"><font size="+0"><font size="3">如果讑֮一?font size="+0">resize</font>ҎQ一直增加长方Ş的宽度,直到增加到宽度超q高度才可以?/font></font></font></p> <p><font size="3"><font size="+0"><font size="3">那么如果针对子类正方形的对象调用<font size="+0">resize</font>ҎQ这个方法会D正方形的边不断地增加下去Q直到溢Zؓ止。换a之,里氏法则被破坏掉了?/font></font></font></p> <p><font size="3"><font size="+0"><u><font size="3">q个例子很重要,它意味着里氏代换与通常的数学法则和生活常识有不可؜淆的区别?/font></u></font></font></p> <p><font size="3"><font size="+0"><font size="3">(LY 注:常识认ؓQ正方Şis a 长方形,而且是一cȝD的长方形。但是在q里Z问题Q如果我们系l中不会有这Lresize操作Q是否正方Ş可以作为长方Ş的子cM呢?看后文是可以?font size="+0">)</font></font></font></font></p> <p><font size="3"><font size="+0"><strong><font size="3">代码的重?/font></strong></font></font></p> <p><font size="3"><font size="+0"><font size="3">长方形和正方形到底应该是什么关pdQ?/font></font></font></p> <p><font size="3"><font size="+0"><font size="3">它们应该都是四边形类的子cR四边ŞcM没有赋值方法,因类g文的<font size="+0">resize()</font>Ҏ不可能适用于四边Şc,而只能只用于不同的具体子c长方Ş和正方Ş。因此里氏代换原则不会被破坏。(LY注:针对需要赋值操作的情况Q?/font></font></font></p> <p><font size="3"><font size="+0"><strong><font size="3">从抽象类l承</font></strong></font></font></p> <p><font size="3"><font size="+0"><u><font size="3">应尽量从抽象cȝ承,而不是从具体cȝѝ?/font></u></font></font></p> <p><font size="3"><font size="+0"><font size="3">上文寚w方Ş和正方Ş的重构用了重构的第一U方法。增加一个抽象类Q让两个具体c都成ؓ抽象cȝ子类?/font></font></font></p> <p><font size="3"><font size="+0"><font size="3"><u>C一条指导性的原则Q如果有一个由l承关系形成的等U结构的话,在等U结构树图上的所有树叶节炚w应当是具体类Q而所有的树枝节点都应当是抽象cL?font size="+0">Java</font></u><u>接口?/u></font></font></font></p> <p><font size="3"><font size="+0"><font size="3">问答?/font></font></font></p> <p><font size="3"><font size="+0"><font size="+0"><font size="3">1?/font>  </font><font size="3">一个有名的思L题,<font size="+0">filename</font>能不能作?font size="+0">string</font>cȝ子类Q?/font></font></font></p> <p><font size="3"><font size="+0"><font size="3">{:不能?font size="+0">Filename</font>对象不能实现<font size="+0">string</font>对象的所有行为。比如两?font size="+0">string</font>对象相加可以l出一个新的有效的<font size="+0">string</font>对象。而两?font size="+0">filename</font>对象相加未必会得C个新的有效的<font size="+0">Filename</font>对象?/font></font></font></p> <p><font size="3"><font size="+0"><font size="3"><font size="+0">       </font>另外Q?font size="+0">Java</font>中的<font size="+0">String</font>cd?font size="+0">final</font>cdQ因此不可以l承?/font></font></font></p> <p><font size="3"><font size="+0"><font size="+0"><font size="3">2?/font>  </font><font size="3">如果正方形的辚w不会发生改变Q是否可以成为长方Ş的子cdQ(LY注:不变正方形,是辚w不会发生变化的正方ŞQ也是遵守不变模式的正方Ş。不?Immutable)模式,一个对象在对象在创Z后就不再变化。)</font></font></font></p> <p><font size="3"><font size="+0"><font size="3">{:可以。实现时Q父cL两个属性宽度和高度。子cL三个属性宽度、高度和辏V针Ҏ一个属性,包含一个内部变量,一?font size="+0">Set</font>值方法,一?font size="+0">Get</font>值方法。子cL方Ş只需要将<font size="+0">Set</font>值方法不写Q何语句即可?/font></font></font></p> <p><font size="3"><font size="+0"><font size="+0"><font size="3">3?/font>  </font><font size="3">从里式代换角度看<font size="+0">Java</font>?font size="+0">Properties</font>?font size="+0">Hashtable</font>的关pL否合适?</font></font></font></p> <p><font size="3"><font size="+0"><font size="3">{:不合适。在<font size="+0">Java</font>中,<font size="+0">Properties</font>?font size="+0">Hashtable</font>的子cR显?font size="+0">Properties</font>是一U特D的<font size="+0">Hashtable,</font>它只接受<font size="+0">string</font>cd的键Q?font size="+0">Key</font>Q和|<font size="+0">Value</font>Q。但是,父类<font size="+0">Hashtable</font>可以接受Mcd的键和倹{这意味着Q在一些需要非<font size="+0">String</font>cd的键和值的地方Q?font size="+0">Properties</font>不能取代<font size="+0">Hashtable</font>?/font></font></font></p> <p><font size="3"><font size="+0"><font size="3">QLY注:合成/聚合复用原则中有更详l的讨论Q应使用合成/l合而不是ѝ它们是has a的关p而不是is a的关pR)</font></font></font></p> <p><br /> </p> <p><br /> </p> <p><br /> </p> <font size="3">另外Q有一有意思的文章不赞同这个观点:<br /> </font> <p>本文假定读者已l了解有x方Ş不是长方形的相关内容?br />         <br />         之前Z讨论的正方Ş长方形的问题的关? 在哪里?我觉得就在于改动长方形的边的长度。我们可以这么考虑一下,一个长方Ş的instance的边长应该是可变的吗Q我觉得一旦一个长方Ş的边长改? 之后它就成了另一个长方Ş了(一个新的instanceQ。所以长方Şc里面不应该有改变其辚w的方法,一个长方Ş实例各个的边长应当在new它的时候确 定下来,q且它们应当是immutable的。基于这U考虑Q我设计的长方Ş和正方Ş的类如下所C:<br /> //长方?br /> public class Rectangle {<br />   private final int width;<br />   private final int height;<br />   <br />   public Rectangle(int width, int height) {<br />     this.width = width;<br />     this.height = height;<br />   }<br />   <br />   public int getWidth() {<br />     return width;<br />   }</p> <p>  public int getHeight() {<br />     return height;<br />   }<br />   <br />   public int getArea() {<br />     return width*height;<br />   }<br /> }</p> <p>//正方?br /> public class Square extends Rectangle{<br />   private final int side;<br />   <br />   public Square(int side) {<br />     super(side, side);<br />     this.side = side;<br />   }<br />   <br />   public int getSide() {<br />     return side;<br />   }<br /> }</p> <p>        q种l承关系既W合现实中的父子关系也遵循LSP。之所以这么设计,我的x是一个类所h的方法不应当能够改变其本质。比如有一个Menc,它可以有 eat(),sleep(),work(),makeLovewith(Person p)ҎQ但是如果你在里面定义denatureToWomen(),denatureToEunuch()很不恰当了Q因改变了其本质Q导致这? Men的实例不再属于Menc(臛_已经和现实不dQ了。除非这两个Ҏ不能改变该实例本质,否则在Men里面定义q两个方法本w就是有问题的。不q如 果用下面q种方式定义也许可行Q?br /> public Women denatureToWomen() {<br />   Women w = new Women();<br />   //set attributes here<br />   return w;<br /> }</p> <p>public Eunuch denatureToEunuch() {<br />   Eunuch e = new Eunuch();<br />   //set attributes here<br />   return e;<br /> }</p> <p>q样一来,调用denatureToWomen()会生一个新的实例,原来的那个Men实例依然存在Q这和现实生zM然不dQ现实生zM一个实例不光可以上?upcast)Q还可以q型,寒。。?/p> <p>M一句话Q一个类的方法不应该改变其实例的本质?/p> <br /> <img src ="http://www.aygfsteel.com/sealyu/aggbug/307045.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/sealyu/" target="_blank">seal</a> 2009-12-23 15:39 <a href="http://www.aygfsteel.com/sealyu/archive/2009/12/23/307045.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式原则详解Q{Q?/title><link>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307043.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 23 Dec 2009 07:34:00 GMT</pubDate><guid>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307043.html</guid><wfw:comment>http://www.aygfsteel.com/sealyu/comments/307043.html</wfw:comment><comments>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307043.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/sealyu/comments/commentRss/307043.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/sealyu/services/trackbacks/307043.html</trackback:ping><description><![CDATA[<p>q篇文章,不需要你一ơ就看懂,如果你真的能一ơ都看懂,我想设计模式对于你来? 已经没什么难度了..因ؓ设计模式是要体现这些原则的,你可以把设计原则看做是一门语a,设计模式是由q些语言~码的程?.你既然已l明?_N了? a,剩下的编码自然是很简单的事情,~码的越多则l验多,l验多则对原则的理解就深...q是一?span style="color: red;">学习领悟</span>的过E?.</p> <p>     我希望这文章能帮助Ch感受?span style="color: red;">设计模式的乐?/span>,避免重复~? ?...减少力_?.如果你能在用心静静的体会文章的每个字,每段话的意?q样可以避免走很多弯?..我以前学习设计模式的时?是因ؓ忽略? 原则,凭着感觉,看着书来学习设计模式,l果是知其然而不知其所以然....如果你是初学设计模式,再了解了OOP的三大原?套,l承,多?? ?请反复的l合原则,来学习设计模?.q样可以辑ֈ事半功倍的效果...<br />   <br />      <span style="color: red;">设计模式的核心原则是:"开-?原则(  Open - Closed Principle ~写:OCP  ),一切的一切都是围l着"开-?原则展开?.<br /> </span>     意思是,在一个系l中,对于扩展是开攄,对于修改是关闭的,一个好的系l是在不修改源代码的情况?可以扩展你的功能..而实现开闭原则的关键是抽象?<br />      ?开-?原则?不允怿改的是抽象的cL者接?允许扩展的是具体的实现类,抽象cd接口?开-?原则中扮演着极其重要的角?.卌预知可能变化的需?又预见所有可能已知的扩展..所以在q里"抽象?是关?!!</p> <p>      <span style="color: red;">可变性的闭原则:扑ֈpȝ的可变因?它装h..</span>q是 ?开-?原则最好的实现..不要把你的可变因素放在多个类?或者散落在E序的各个角?.你应该将可变的因?套h..q且切忌不要把所用的? 变因素封套在一?.最好的解决办法?分块套你的可变因素!!避免大c?长c?长Ҏ的出?!l你的程序增加艺术气?程序艺术化是我们的 目标!!</p> <p>      <span style="color: red;">里氏代换原则:M基类可以出现的地?子类也可以出?.</span>如果你通读q?lt;Java~程思想>,我想你应该明白这个原?在书?Bruce Eckel 大师用了大量的章节来讲解"向上转型"?向下转型",我想目的很清?不仅是要你明白类的型?更重要的是要你明白父cM子类的关p?.<br /> <br />       <span style="color: red;">依赖倒{原则:要依赖抽?而不要依赖具体的实现..</span>? 果说开闭原则是目标,依赖倒{原则是到?开?原则的手D?.如果要达到最好的"开?原则,p量的遵守依赖倒{原则..可以说依赖倒{原则是对" 抽象?的最好规?!我个人感?依赖倒{原则也是里氏代换原则的补?.你理解了里氏代换原则,再来理解依赖倒{原则应该是很Ҏ?.<br />   合成/聚合原则:要尽量用合?聚合原则,而不是承关p达到Y件复用的目的..此原则和里氏代换原则氏相辅相成的,两者都是具体实?开-?原则的规?.q反q一原则:无法实?开-?原则..先来看看什么是合成,什么是聚合.</p> <p>      <span style="color: red;">什么是合成?<br /> </span>     合成:是指一个整体对依托他而存在的关系,例如:一个h对他的房子和家具,其中他的房子和家h不能被共享的,因ؓ那些东西都是他自q..q且人没? q个也关pd没了..q个例子好?乌鸡癑և丸这个?它是有乌鸡和上等药材合成而来的一?.也比如网l游戏中的武器装备合成一?多种东西合ƈ? 一U超强的东西一?.<br />       <br />       <span style="color: red;">什么是聚合?<br /> </span>      聚合:聚合是比合成关系的一U更强的依赖关系,聚合是一个整体对个体的部?例如,一个奔驰S360汽R,对奔驰S360引擎,奔驰S360轮胎的关 p?.q些关系是带有聚合性质?.因ؓ奔驰S360引擎和奔驰S360轮胎他们只能被奔驰S360汽R所?d了奔驰S360汽R,它们失M? 在的意义..在我们的设计?q样的关pM应该频繁出现..q样会增大设计的耦合?.<br />       明白了合成和聚合关系,再来理解合成/聚合原则应该清楚了..要避免在pȝ设计中出?一个类的承层ơ超q??.如果q样的话,可以考虑重构你的代码,或者重新设计结?.当然最好的办法是考虑使用合成/聚合原则...<br /> <br />       <span style="color: red;">q米Ҏ?pȝ中的c?量不要与其他类互相作用,减少cM间的耦合?/span>, 因ؓ在你的系l中,扩展的时?你可能需要修改这些类,而类与类之间的关p?军_了修改的复杂?怺作用多,则修攚w度就大,反之,如果怺作用? 小,则修改v来的隑ֺp?.例如AcM赖Bc?则BcM赖Cc?当你在修改Acȝ时?你要考虑BcL否会受到影响,而Bcȝ影响是否又会影响到C c?.如果此时Ccd依赖Dcȝ?呵呵,我想q样的修Ҏ的受?.<br /> <br />      <span style="color: red;">接口隔离法则:q个法则与_c特法则是相通的,</span>q米Ҏ则是目的,而接口隔L则是对_c特法则的规?.Z做到可能小的耦合?我们需要用接口来规范c?用接口来U束c?要达到_c特法则的要?最好就是实现接口隔L?实现接口隔离法则,你也满了q米Ҏ?..<br />      <br />  <br />        如果你能看这?说明你已l对q些原则了有了感性的认识..q些原则是设计模式的核心,如果不能充分理解q些原则,是很隄解好设计模式?.</p> <p>      如果W一遍看不懂,没关p??span style="color: red;"><strong>反复揣摩,l读每个?每句?.</strong></span>对于q些原则,我也是看了N*N遍才明白?q期间也没Q何h指点q我,更每的这么细,奖励自己一下先<img src="http://www.cnblogs.com/Emoticons/QQ/05.gif" width="20" border="0" height="20" alt="" />, 汗啊)..我推荐看完原则之?L设计模式,看两三个模式,然后理解一?自己动手把模式实现出?再回头来看原?你会感觉,你的模式一定是满了其 中的某些原则!!q是必然?!只要你理解了原则,设计模式不难理解..好?有了内功基础的你,再来学习刀,?枪这些武器的时?要比那些直接学习 刀,?剑的?快很?效果也好很多...<br /> <br />     如果你要了解设计模式,在园子里也有N多好的设计模式文?_֍区的设计模式?.有很多都是不错的介绍文章..另外q段旉,我也在发表关于设计模式的心得..如果有兴?可以x一?..<img src="http://www.cnblogs.com/Emoticons/QQ/14.gif" width="20" border="0" height="20" alt="" /></p> <img src ="http://www.aygfsteel.com/sealyu/aggbug/307043.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/sealyu/" target="_blank">seal</a> 2009-12-23 15:34 <a href="http://www.aygfsteel.com/sealyu/archive/2009/12/23/307043.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式--W记--职责链模?/title><link>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307041.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 23 Dec 2009 07:09:00 GMT</pubDate><guid>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307041.html</guid><wfw:comment>http://www.aygfsteel.com/sealyu/comments/307041.html</wfw:comment><comments>http://www.aygfsteel.com/sealyu/archive/2009/12/23/307041.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/sealyu/comments/commentRss/307041.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/sealyu/services/trackbacks/307041.html</trackback:ping><description><![CDATA[<h2>一?nbsp;职责链(Chain of ResponsibilityQ模?/h2> <p>责Q链模式是一U对象的行ؓ模式【GOF95】。在责Q链模式里Q很多对象由每一个对象对其下家的引用而连接v来Ş成一条链。请求在q个链上传递, 直到链上的某一个对象决定处理此h。发个请求的客户端ƈ不知道链上的哪一个对象最l处理这个请求,q得系l可以在不媄响客L的情况下动态地重新 l织铑֒分配责Q?/p> <p><strong>从击鼓传p?/strong></p> <p>击鼓传花是一U热闹而又紧张的饮酒游戏。在酒宴上宾客依ơ坐定位|,׃人击鼓,击鼓的地方与传花的地Ҏ分开的,以示公正。开始击鼓时Q花束就开始依ơ传递,鼓声一落,如果花束在某人手中,则该人就得饮酒?/p> <p>击鼓传花便是责Q链模式的应用。责任链可能是一条直Uѝ一个环链或者一个树l构的一部分?/p> <h2><br /> 二?nbsp;责Q链模式的l构</h2> <p>责Q链模式涉及到的角色如下所C:</p> <p><img src="http://zhenyulu.cnblogs.com/images/cnblogs_com/zhenyulu/Pic98.gif" width="319" border="0" height="190" alt="" /></p> <p><font color="#000080">抽象处理?Handler)角色Q?/font>定义Z个处理请求的接口。如果需要,接口可以定义Z个方法,以设定和q回对下家的引用。这个角色通常׃个抽象类或接口实现?/p> <p><font color="#000080">具体处理?ConcreteHandler)角色Q?/font>具体处理者接到请求后Q可以选择请求处理掉Q或者将h传给下家。由于具体处理者持有对下家的引用,因此Q如果需要,具体处理者可以访问下家?/p> <h2><br /> </h2> <br /> <div class="wmqeeuq" id="tf_edit_html_title" class="tf_edit_html_title">Chain of Responsibility模式的实?/div> 要实现Chain of Responsibility模式Q需要满模式的基本要Ӟ<br /> 1Q对象链的组l。需要将某Q务的所有职责执行对象以铄形式加以l织?br /> 2Q消息或h的传递。将消息或请求沿着对象链传递,以让处于对象链中的对象得到处理机会?br /> 3Q处于对象链中的对象的职责分配。不同的对象完成不同的职责?br /> 4QQ务的完成。处于对象链的末对象l束dq停止消息或h的l传递?br /> <br /> <div class="wmqeeuq" id="tf_edit_html_title" class="tf_edit_html_title">Chain of Responsibility模式的优优缺点:</div> <strong>优点Q?/strong><br /> 1Q责ȝ分担。每个类只需要处理自p处理的工作(不该处理的传递给下一个对象完成)Q明各cȝ责Q范围Q符合类的最封装原则?br /> 2Q可以根据需要自q合工作流E。如工作程发生变化Q可以通过重新分配对象链便可适应新的工作程?br /> 3Q类与类之间可以以松耦合的Ş式加以组l?br /> <strong>~点Q?/strong><br /> 因ؓ处理时以铄形式在对象间传递消息,Ҏ实现方式不同Q有可能会媄响处理的速度?br /> <br /> <br /> <br /> 例子Q?br /> <div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><span style="color: #0000ff;">namespace</span><span style="color: #000000;"> ChainOfResponsibility_DesignPattern<br /> {<br />     </span><span style="color: #0000ff;">using</span><span style="color: #000000;"> System;<br /> <br />     </span><span style="color: #0000ff;">abstract</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Handler <br />     {<br />         </span><span style="color: #0000ff;">protected</span><span style="color: #000000;"> Handler successorHandler;<br />         </span><span style="color: #0000ff;">abstract</span><span style="color: #000000;"> </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> HandleRequest(Request request);        <br />         </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> SetSuccessor(Handler sucessor)<br />         {<br />             successorHandler </span><span style="color: #000000;">=</span><span style="color: #000000;"> sucessor;<br />         }<br />     }<br /> <br />     </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> ConcreteHandler1 : Handler<br />     {<br />         </span><span style="color: #0000ff;">override</span><span style="color: #000000;"> </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> HandleRequest(Request request)<br />         {<br />             </span><span style="color: #008000;">//</span><span style="color: #008000;"> determine if we can handle the request</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (request.RequestType </span><span style="color: #000000;">==</span><span style="color: #000000;"> </span><span style="color: #000000;">1</span><span style="color: #000000;">) </span><span style="color: #008000;">//</span><span style="color: #008000;"> some complex decision making!</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">            {<br />                 </span><span style="color: #008000;">//</span><span style="color: #008000;"> request handling code goes here</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">                Console.WriteLine(</span><span style="color: #000000;">"</span><span style="color: #000000;">request handled in ConcreteHandler1</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />             }<br />             </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> <br />             {<br />                 </span><span style="color: #008000;">//</span><span style="color: #008000;"> not handled here - pass on to next in the chain</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">                </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (successorHandler </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">)<br />                     successorHandler.HandleRequest(request);<br />             }<br />         }<br />     }<br /> <br />     </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> ConcreteHandler2 : Handler<br />     {<br />         </span><span style="color: #0000ff;">override</span><span style="color: #000000;"> </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> HandleRequest(Request request)<br />         {<br />             </span><span style="color: #008000;">//</span><span style="color: #008000;"> determine if we can handle the request</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (request.RequestType </span><span style="color: #000000;">==</span><span style="color: #000000;"> </span><span style="color: #000000;">2</span><span style="color: #000000;">) </span><span style="color: #008000;">//</span><span style="color: #008000;"> some complex decision making!</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">            {<br />                 </span><span style="color: #008000;">//</span><span style="color: #008000;"> request handling code goes here</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">                Console.WriteLine(</span><span style="color: #000000;">"</span><span style="color: #000000;">request handled in ConcreteHandler2</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />             }<br />             </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> <br />             {<br />                 </span><span style="color: #008000;">//</span><span style="color: #008000;"> not handled here - pass on to next in the chain</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">                </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (successorHandler </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">)<br />                     successorHandler.HandleRequest(request);<br />             }<br />         }<br />     }<br /> <br />     </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> ConcreteHandler3 : Handler<br />     {<br />         </span><span style="color: #0000ff;">override</span><span style="color: #000000;"> </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> HandleRequest(Request request)<br />         {<br />             </span><span style="color: #008000;">//</span><span style="color: #008000;"> determine if we can handle the request</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (request.RequestType </span><span style="color: #000000;">==</span><span style="color: #000000;"> </span><span style="color: #000000;">3</span><span style="color: #000000;">) </span><span style="color: #008000;">//</span><span style="color: #008000;"> some complex decision making!</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">            {<br />                 </span><span style="color: #008000;">//</span><span style="color: #008000;"> request handling code goes here</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">                Console.WriteLine(</span><span style="color: #000000;">"</span><span style="color: #000000;">request handled in ConcreteHandler3</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />             }<br />             </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> <br />             {<br />                 </span><span style="color: #008000;">//</span><span style="color: #008000;"> not handled here - pass on to next in the chain</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">                </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (successorHandler </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">)<br />                     successorHandler.HandleRequest(request);<br />             }        <br />         }<br />     }<br /> <br />     </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Request <br />     {<br />         </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> iRequestType;<br />         </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">string</span><span style="color: #000000;"> strRequestParameters;<br /> <br />         </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> Request(</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> requestType, </span><span style="color: #0000ff;">string</span><span style="color: #000000;"> requestParameters)<br />         {<br />             iRequestType </span><span style="color: #000000;">=</span><span style="color: #000000;"> requestType;    <br />             strRequestParameters </span><span style="color: #000000;">=</span><span style="color: #000000;"> requestParameters;<br />         }<br /> <br />         </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> RequestType <br />         {<br />             </span><span style="color: #0000ff;">get</span><span style="color: #000000;"> <br />             {<br />                 </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> iRequestType;<br />             }<br />             </span><span style="color: #0000ff;">set</span><span style="color: #000000;"> <br />             {<br />                 iRequestType </span><span style="color: #000000;">=</span><span style="color: #000000;"> value;<br />             }<br />         }<br />     }<br /> <br />     </span><span style="color: #808080;">///</span><span style="color: #008000;"> </span><span style="color: #808080;"><summary></span><span style="color: #008000;"><br />     </span><span style="color: #808080;">///</span><span style="color: #008000;">    Summary description for Client.<br />     </span><span style="color: #808080;">///</span><span style="color: #008000;"> </span><span style="color: #808080;"></summary></span><span style="color: #808080;"><br /> </span><span style="color: #000000;">    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Client<br />     {<br />         </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> Main(</span><span style="color: #0000ff;">string</span><span style="color: #000000;">[] args)<br />         {<br />             </span><span style="color: #008000;">//</span><span style="color: #008000;"> Set up chain (usually one need to be done once)</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">            Handler firstHandler </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> ConcreteHandler1();<br />             Handler secondHandler </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> ConcreteHandler2();<br />             Handler thirdHandler </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> ConcreteHandler3();<br />             firstHandler.SetSuccessor(secondHandler);<br />             secondHandler.SetSuccessor(thirdHandler);<br /> <br />             </span><span style="color: #008000;">//</span><span style="color: #008000;"> After setting up the chain of responsibility, we can<br />             </span><span style="color: #008000;">//</span><span style="color: #008000;"> now generate requests and pass then off to the <br />             </span><span style="color: #008000;">//</span><span style="color: #008000;"> chain to be handled<br /> <br />             </span><span style="color: #008000;">//</span><span style="color: #008000;"> generate and fire request</span><span style="color: #008000;"><br /> </span><span style="color: #000000;">            Request newRequest </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Request(</span><span style="color: #000000;">2</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">This are the request parameters</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />             firstHandler.HandleRequest(newRequest);<br />             <br />             </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />         }<br />     }<br /> }<br /> </span></div> <br /> <br /> <img src ="http://www.aygfsteel.com/sealyu/aggbug/307041.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/sealyu/" target="_blank">seal</a> 2009-12-23 15:09 <a href="http://www.aygfsteel.com/sealyu/archive/2009/12/23/307041.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式--W记--装饰模式http://www.aygfsteel.com/sealyu/archive/2009/12/23/307034.htmlsealsealWed, 23 Dec 2009 06:15:00 GMThttp://www.aygfsteel.com/sealyu/archive/2009/12/23/307034.htmlhttp://www.aygfsteel.com/sealyu/comments/307034.htmlhttp://www.aygfsteel.com/sealyu/archive/2009/12/23/307034.html#Feedback0http://www.aygfsteel.com/sealyu/comments/commentRss/307034.htmlhttp://www.aygfsteel.com/sealyu/services/trackbacks/307034.html 装饰模式Q?/span>DecoratorQ也叫包装器模式Q?/span>WrapperQ?/span>GOF在《设计模式》一书中l出的定义ؓQ动态地l一个对象添加一些额外的职责。就增加功能来说Q?/span>Decorator模式相比生成子类更ؓ灉|?br /> 装饰模式的组成:

1)        抽象构g角色Q?/span>ComponentQ:定义一个抽象接口,以规范准备接攉加责ȝ对象?/span>

2)        具体构g角色(Concrete Component)Q这是被装饰者,定义一个将要被装饰增加功能的类?/span>

3)        装饰角色(Decorator)Q持有一个构件对象的实例Qƈ定义了抽象构件定义的接口?/span>

4)        具体装饰角色(Concrete Decorator)Q负责给构gd增加的功能?/span>

JAVA?/span>IO的设计大量运用了装饰模式。看看我们熟悉的代码:


装饰模式的特点;
Q?Q?装饰对象和真实对象有相同的接口。这样客L对象可以以和真实对象相同的方式和装饰对象交互?br /> Q?Q?装饰对象包含一个真实对象的索引QreferenceQ?br /> Q?Q?装饰对象接受所有的来自客户端的h。它把这些请求{发给真实的对象?br /> Q?Q?装饰对象可以在{发这些请求以前或以后增加一些附加功能。这样就保了在q行Ӟ不用修改l定对象的结构就可以在外部增加附加的功能。在面向对象的设计中Q通常是通过l箋来实现对l定cȝ功能扩展?br />

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("..")));

层层包装Q增强功能。这是装饰模式的要旨?/font>




seal 2009-12-23 14:15 发表评论
]]>
设计模式Q简单工厂、工厂方法、抽象工厂之结与区?(?http://www.aygfsteel.com/sealyu/archive/2009/12/10/305454.htmlsealsealThu, 10 Dec 2009 09:01:00 GMThttp://www.aygfsteel.com/sealyu/archive/2009/12/10/305454.htmlhttp://www.aygfsteel.com/sealyu/comments/305454.htmlhttp://www.aygfsteel.com/sealyu/archive/2009/12/10/305454.html#Feedback0http://www.aygfsteel.com/sealyu/comments/commentRss/305454.htmlhttp://www.aygfsteel.com/sealyu/services/trackbacks/305454.html单工厂,工厂ҎQ抽象工厂都属于设计模式中的创徏型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来Q优化了pȝ的架构,q且增强了系l的扩展性?/p>

本文是本人对q三U模式学习后的一个小l以及对他们之间的区别的理解?/p>

单工?/strong>

单工厂模式的工厂cM般是使用静态方法,通过接收的参数的不同来返回不同的对象实例?/p>

不修改代码的话,是无法扩展的?/p>

单工? />
</p>
<p><strong>工厂Ҏ</strong>
</p>
<p>工厂Ҏ是针Ҏ一U品提供一个工厂类。通过不同的工厂实例来创徏不同的品实例?/p>
<p>在同一{l构中,支持增加L产品?/p>
<p><img src=

抽象工厂

抽象工厂是应对品族概念的。比如说Q每个汽车公司可能要同时生轿RQ货车,客RQ那么每一个工厂都要有创徏轿RQ货车和客R的方法?/p>

应对产品族概念而生Q增加新的品线很容易,但是无法增加新的产品?/p>

抽象工厂

★工厂模式中Q重要的是工厂类Q而不是品类。品类可以是多UŞ式,多层l承或者是单个c都是可以的。但要明的Q工厂模式的接口只会q回一U类型的实例Q这是在设计产品cȝ时候需要注意的Q最好是有父cL者共同实现的接口?/p>

★用工厂模式,q回的实例一定是工厂创徏的,而不是从其他对象中获取的?/p>

★工厂模式返回的实例可以不是新创建的Q返回由工厂创徏好的实例也是可以的?/p>

区别

单工?/span> Q?/span> 用来生同一{l构中的L产品。(对于增加新的产品Q无能ؓ力)

工厂模式 Q用来生产同一{l构中的固定产品。(支持增加L产品Q? 
抽象工厂 Q用来生产不同品族的全部品。(对于增加新的产品Q无能ؓ力;支持增加产品族)  

以上三种工厂 Ҏ在等U结构和产品族这两个方向上的支持E度不同。所以要Ҏ情况考虑应该使用哪种Ҏ? 



seal 2009-12-10 17:01 发表评论
]]>
UML中的几种关系http://www.aygfsteel.com/sealyu/archive/2009/12/10/305420.htmlsealsealThu, 10 Dec 2009 06:47:00 GMThttp://www.aygfsteel.com/sealyu/archive/2009/12/10/305420.htmlhttp://www.aygfsteel.com/sealyu/comments/305420.htmlhttp://www.aygfsteel.com/sealyu/archive/2009/12/10/305420.html#Feedback0http://www.aygfsteel.com/sealyu/comments/commentRss/305420.htmlhttp://www.aygfsteel.com/sealyu/services/trackbacks/305420.html      (1)依赖(dependency)是两个事物间的语义关p,其中一个事?独立事物)发生变化会媄响另一个事?依赖事物)的语义。在囑Ş上,把一个依赖画成一条可能有方向的虚Uѝ?
     (2)兌(association)是一U结构关p,q接模型元素及链接实例,用一条实U来表示?
     (3)泛化(generalization)是一U特D/一般关p,Ҏ元素(子元?的对象可替代一般元?父元?的对象,用这U方法,子元素共享了 父元素的l构和行为。在囑Ş上,把一个泛化关pȝ成一条带有空心箭头的实线Q它指向父元素?
     (4)实现(realization)是类元之间的语义关系Q其中一个类元指定了由另一个类元保证执行的契约。在两种地方要遇到实现关p:一U是在接口和 实现它们的类或构件之_另一U是在用例和实现它们的协作之间。在囑Ş上,把一个实现关pȝ成一条带有空心箭头的虚线?nbsp;
     (5)聚集(aggregation)表示整体与部分的关系Q用一条实U加I心菱Ş来表C;
     (6)l成(Composition)表示整体与部分的有一关系Q用一条实U加实心菱Ş来表C;


UML中几U类间关p:l承、实现、依赖、关联、聚合、组合的联系与区?/p>

l承

指的是一个类Q称为子cR子接口Q承另外的一个类Q称为父cR父接口Q的功能Qƈ可以增加它自q新功能的能力Q承是cMcL者接口与接口之间最常见的关p;在Java中此cdp通过关键字extends明确标识Q在设计时一般没有争议性;

实现

指的是一个classcd现interface接口Q可以是多个Q的功能Q实现是cM接口之间最常见的关p;在Java中此cdp通过关键字implements明确标识Q在设计时一般没有争议性;

依赖

可以单的理解Q就是一个类A使用C另一个类BQ而这U用关pLh偶然性的、、时性的、非常弱的,但是Bcȝ变化会媄响到AQ比如某q河Q需要借用一条船Q此时h与船之间的关pd是依赖;表现在代码层面,为类B作ؓ参数被类A在某个methodҎ中用;

他体现的是两个类、或者类与接口之间语义别的一U强依赖关系Q比如我和我的朋友;q种关系比依赖更强、不存在依赖关系的偶然性、关pM不是临时? 的,一般是长期性的Q而且双方的关pM般是q等的、关联可以是单向、双向的Q表现在代码层面Qؓ被关联类B以类属性的形式出现在关联类A中,也可能是兌 cA引用了一个类型ؓ被关联类B的全局变量Q?br />

聚合

聚合是关联关pȝ一U特例,他体现的是整体与部分、拥有的关系Q即has-a的关p,此时整体与部分之间是可分ȝQ他们可以具有各自的生命周期Q? 部分可以属于多个整体对象Q也可以为多个整体对象共享;比如计算ZCPU、公怸员工的关pȝQ表现在代码层面Q和兌关系是一致的Q只能从语义U别? 区分Q?br />

l合

l合也是兌关系的一U特例,他体现的是一Ucontains-a的关p,q种关系比聚合更强,也称为强聚合Q他同样体现整体与部分间的关p,但此 时整体与部分是不可分的,整体的生命周期结束也意味着部分的生命周期结束;比如你和你的大脑Q表现在代码层面Q和兌关系是一致的Q只能从语义U别来区 分;

对于l承、实现这两种关系没多疑问,他们体现的是一U类与类、或者类与接口间的纵向关p;其他的四者关pd体现的是cMcR或者类与接口间的引 用、横向关p,是比较难区分的,有很多事物间的关p要惛_备定位是很难的,前面也提刎ͼq几U关p都是语义别的Q所以从代码层面q不能完全区分各U关 p;但ȝ来说Q后几种关系所表现的强q度依ơؓQ组?gt;聚合>兌>依赖Q?/p>

seal 2009-12-10 14:47 发表评论
]]>
q米Ҏ则(Law of DemeterQ?/title><link>http://www.aygfsteel.com/sealyu/archive/2009/12/10/305415.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 10 Dec 2009 06:16:00 GMT</pubDate><guid>http://www.aygfsteel.com/sealyu/archive/2009/12/10/305415.html</guid><wfw:comment>http://www.aygfsteel.com/sealyu/comments/305415.html</wfw:comment><comments>http://www.aygfsteel.com/sealyu/archive/2009/12/10/305415.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/sealyu/comments/commentRss/305415.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/sealyu/services/trackbacks/305415.html</trackback:ping><description><![CDATA[<p><font color="#ff0000">q米Ҏ则(LoDQ:</font>又称<font color="#ff0000">最知识原则(LKPQ,</font>是说一个对象应当对其他对象可能少的了解?br /> </p> <p><br /> <font color="#ff0000">狭义的_c特法则:</font></p> <p>如果两个cM必彼此直接通信,那么q两个类׃应当发生直接的相互作?如果其中一个类需要调用另一个类的方法的?可以通过W三者{发这个调?</p> <p><font color="#ff0000">~点:</font></p> <p>会在pȝ内造出大量的小Ҏ,散落在系l的各个角落.q些Ҏ仅仅是传递间接的调用,因此pȝ与系l中的商业逻辑无关.当设计师试图从一张类囄出M的构架时,q些方法会造成qh和困?</p> <p>Z克服狭义q米Ҏ则的~点,可以使用<font color="#ff0000">依赖倒{原则</font>,引入一个抽象的cd引用"抽象陌生?对象,?某h"依赖?抽象陌生?,换言?是?抽象陌生?变成朋友.<br /> </p> <p><br /> <font color="#ff0000">q义的_c特法则:</font></p> <p>一个模块设计得好坏的一个重要的标志是该模块在多大的程度上自q内部数据与实现有关的l节隐藏h.</p> <p>信息的隐藏非帔R要的原因在于,它可以各个子系l之间脱?从而允许它们独立地被开?优化,使用阅读以及修改.</p> <p>q米Ҏ则的主要用意是控制信息的q蝲.在运用_c特法则到系l的设计中时,要注意以下几?</p> <p>* 在类的划分上,应当创徏有弱耦合的类.cM间的耦合弱,p有利于复?</p> <p>* 在类的结构设计上,每一个类都应当尽量降低成员的讉K权限. </p> <p>* 在类的设计上,只要可能,一个类应当设计成不变类.</p> <p>* 在对其他cȝ引用?一个对象对其他对象的引用应降到最?</p> <p>* 量限制局部变量的有效范围.</p> <img src ="http://www.aygfsteel.com/sealyu/aggbug/305415.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/sealyu/" target="_blank">seal</a> 2009-12-10 14:16 <a href="http://www.aygfsteel.com/sealyu/archive/2009/12/10/305415.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DCLQ双锁?Double Checked LockQ安全性分??http://www.aygfsteel.com/sealyu/archive/2009/12/09/305301.htmlsealsealWed, 09 Dec 2009 09:01:00 GMThttp://www.aygfsteel.com/sealyu/archive/2009/12/09/305301.htmlhttp://www.aygfsteel.com/sealyu/comments/305301.htmlhttp://www.aygfsteel.com/sealyu/archive/2009/12/09/305301.html#Feedback1http://www.aygfsteel.com/sealyu/comments/commentRss/305301.htmlhttp://www.aygfsteel.com/sealyu/services/trackbacks/305301.html

? 水木上看C分析DCLQ双锁?Double Checked LockQ安全性的文章。记得以前也讨论q这个问题,但是Z么DCL也存在隐患,至今没弄明白Q这分析做了比较详的解释Q概括v来原因有两点Q一? 因ؓ~译器或者处理器q不是严格按照程序顺序进行指令调度。二是java中同步机制的存在?/p>

原文来自Q?/p>

发信? wyxzellux (I still believe...), 信区: Java
? ? Singleton模式与双锁?DCL)
发信? 水木C֌ (Mon Apr  7 23:42:14 2008), 站内

看OOP教材Ӟ提到了一个双锁?Double-Checked Lock, DCL)的问题,但是书上没有多介l,只是说这是一个和底层内存机制有关的漏z。查阅了下相兌料,对这个问题大致有了点了解?br /> 从头开始说吧?br /> 在多U程的情况下Singleton模式会遇C问题,一个简单的例子

   1:  class Singleton {      
   2:      private static Singleton instance = null;      
   3:        
   4:      public static Singleton instance() {      
   5:          if (instance == null) {      
   6:              instance = new Singleton();      
   7:          }      
   8:          return instance;    
   9:      }    
   10:  }
  
? 设这样一个场景,有两个线E调用Singleton.instance()Q首先线E一判断instance是否{于nullQ判断完后一瞬间虚拟机把U? E二调度行线E,U程二再ơ判断instance是否为nullQ然后创Z个Singleton实例Q线E二的时间片用完后,U程一被唤醒,接下? 它执行的代码依然是instance = new Singleton();
两次调用q回了不同的对象Q出现问题了?br />
最单的Ҏ自然是在c被载入时就初始化这个对象:private static Singleton instance = new Singleton();

JLS(Java Language Specification)中规定了一个类只会被初始化一ơ,所以这样做肯定是没问题的?br />
但是如果要实现gq初始化(Lazy initialization)Q比如这个实例初始化时的参数要在q行期才能确定,应该怎么做呢Q?br />
依然有最单的ҎQ用synchronized关键字修饰初始化ҎQ?br />
    public synchronized static Singleton instance() {        
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
    
q里有一个性能问题Q多个线E同时访问这个方法时Q会因ؓ同步而导致每ơ只有一个线E运行,影响E序性能。而事实上初始化完毕后只需要简单的q回instance的引用就行了?br />
DCL是一?#8220;看似”有效的解x法,先把对应代码放上来吧Q?br />
    1 :   class Singleton {  
    2 :       private static Singleton instance = null ;  
    3 :      
    4 :       public static Singleton instance() {  
    5 :           if (instance == null ) {
    6 :               synchronized (this) {  
    7 :                   if (instance == null)
    8 :                      instance = new Singleton();
    9 :              }
    10 :          }
    11 :          return instance;
    12 :      }
    13 :  }

用JavaWorld上对应文章的标题来评U做法就是smart, but broken。来看原因:

Java ~译器ؓ了提高程序性能会进行指令调度,CPU在执行指令时同样Z性能会ؕ序执行(臛_现在用的大多数通用处理器都是out-of-order的)Q另 外cache的存在也会改变数据回写内存时的顺序[2]。JMM(Java Memory Model, 见[1])指出所有的q些优化都是允许的,只要q行l果和严格按序执行所得的l果一样即可?br />
Java假设每个U程都跑在自q处理? 上,享有自己的内存,和共享的d交互。注意即使在单核上这U模型也是有意义的,考虑到cache和寄存器会保存部分时变量。理Z每个U程修改自己? 内存后,必须立即更新对应的主存内宏V但是Java设计师们认ؓq种U束会媄响程序性能Q他们试着创造了一套让E序跑得更快、但又保证线E之间的交互与预 期一致的内存模型?br />
synchronized关键字便是其中一把利器。事实上Qsynchronized块的实现和Linux中的信号? (semaphore)q是有区别的Q前者过E中锁的获得和释N会都会引发一ơMemory Barrier来强制线E本地内存和d之间的同步。通过q个机制QJava中的同步机制保证了synchronized块中指o的原子? (atomic)?br />
好了Q回q头来看DCL问题。看h讉K一个未同步的instance字段不会产生什么问题,我们再次来假设一个场景:

U程一q入同步块,执行instance = new Singleton(); U程二刚开始执行getResource();

按照序的话Q接下来应该执行的步骤是 1) 分配新的Singleton对象的内?2) 调用Singleton的构造器Q初始化成员字段 3) instance被赋为指向新的对象的引用?br />
? 面说q,~译器或处理器都Z提高性能都有可能q行指o的ؕ序执行,U程一的真正执行步骤可能是1) 分配内存 2) instance指向新对? 3) 初始化新实例。如果线E二?完成?执行前被唤醒Q它看到了一个不为null的instanceQ蟩出方法体CQ带着一个还没初始化? Singleton对象?br />
错误发生的一U情形就是这P关于更详l的~译器指令调度导致的问题Q可以参看这个网?[4]?br />
[3] 中提供了一个编译器指o调度的证?br />
instance = new Singleton(); q条命o在Symantec JIT中被~译?br />
0206106A   mov         eax,0F97E78h
0206106F   call        01F6B210                  ; 分配I间
02061074   mov         dword ptr [ebp],eax       ; EBP中保存了instance的地址

02061077   mov         ecx,dword ptr [eax]       ; 解引用,获得新的指针地址

02061079   mov         dword ptr [ecx],100h      ; 接下来四行是inline后的构造器
0206107F   mov         dword ptr [ecx+4],200h    
02061086   mov         dword ptr [ecx+8],400h
0206108D   mov         dword ptr [ecx+0Ch],0F84030h

可以看到Q赋值完成在初始化之前,而这是JLS允许的?br />  
? 一U情形是Q假讄E一安稳地完成Singleton对象的初始化Q退Z同步块,q同步了和本地内存和d。线E二来了Q看C个非I的引用Q拿走。注 意线E二没有执行一个Read BarrierQ因为它Ҏ没q后面的同步块。所以很有可能此时它看到的数据是陈旧的?br />
q有很多人根据已知的几种提出了一个又一个fix的方法,但最l还是出C更多的问题。可以参阅[3]中的介绍?br />
[5]中还说明了即使把instance字段声明为volatileq是无法避免错误的原因?br />
由此可见Q安全的Singleton的构造一般只有两U方法,一是在c蝲入时创实例Q二是用性能较差的synchronizedҎ?br />
(by ZelluX  http://www.aygfsteel.com/zellux )

参考资料:

[1] Java Language Specification, Second Edition, W?7章介l了Java中线E和内存交互关系的具体细节?br /> [2] out-of-order与cache的介l可以参阅Computer System, A Programmer's Perspective的第四、五章?br /> [3] The "Double-Checked Locking is Broken" Declaration, http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
[4] Synchronization and the Java Memory Model, http://gee.cs.oswego.edu/dl/cpj/jmm.html
[5] Double-checked locking: Clever, but broken, http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1
[6] Holub on Patterns, Learning Design Patterns by Looking at Code



seal 2009-12-09 17:01 发表评论
]]>
重构Q{载) http://www.aygfsteel.com/sealyu/archive/2008/04/10/192013.htmlsealsealThu, 10 Apr 2008 15:00:00 GMThttp://www.aygfsteel.com/sealyu/archive/2008/04/10/192013.htmlhttp://www.aygfsteel.com/sealyu/comments/192013.htmlhttp://www.aygfsteel.com/sealyu/archive/2008/04/10/192013.html#Feedback0http://www.aygfsteel.com/sealyu/comments/commentRss/192013.htmlhttp://www.aygfsteel.com/sealyu/services/trackbacks/192013.html  重构Q?a target="_new">RefactoringQ就是在不改?a target="_new">软g现有功能的基上,通过调整E序代码改善软g的质量、性能Q其程序的设计模式?a target="_new">架构更趋合理Q提高Y件的扩展性和l护性?/font>

  也许有h会问Qؓ什么不在项目开始时多花些时间把设计做好Q而要以后花时间来重构呢?要知道一个完 得可以预见未来M变化的设计,或一个灵zd可以容纳M扩展的设计是不存在的。系l设计h员对卛_着手的目往往只能从大方向予以把控Q而无法知道每 个细枝末节,其次永远不变的就是变化,提出需?/a>的用户往往要在软g成型后,始才开?品头"Q系l设计h员毕竟不是先知先觉的仙Q功能的变化D设计的调整再所隑օ。所?试为先Q持l重?作ؓ良好开发习惯被来多的h所采纳Q测试和重构像黄河的护堤Q成Z证Y件质量的法宝?/font>

一、ؓ什么要重构QRefactoringQ?/strong>

      在不改变pȝ功能的情况下Q改变系l的实现方式。ؓ什么要q么做?投入_֊不用来满_户关心的需求,而是仅仅改变了Y件的实现方式Q这是否是在费客户的投资呢Q?/font>

      重构的重要性要从Y件的生命周期说v。Y件不同与普通的产品Q他是一U智力品,没有具体的物理Ş态。一个Y件不可能发生物理损耗,界面上的按钮永远不会因ؓ按动ơ数太多而发生接触不良。那么ؓ什么一个Y件制造出来以后,却不能永q用下dQ?/font>

      对Y件的生命造成威胁的因素只有一个:需求的变更。一个Y件LxU特定的需求而生,时代在发展,客户的业务也在发生变化。有的需求相对稳定一 些,有的需求变化的比较剧烈Q还有的需求已l消׃Q或者{化成了别的需求。在q种情况下,软g必须相应的改变?/font>

      考虑到成本和旉{因素,当然不是所有的需求变化都要在软gpȝ中实现。但是ȝ说来QY件要适应需求的变化Q以保持自己的生命力?/font>

      q就产生了一U糟p的现象QY件品最初制造出来,是经q精心的设计Q具有良好架构的。但是随着旉的发展、需求的变化Q必M断的修改原有的功能、追? 新的功能Q还免不了有一些缺陷需要修攏Vؓ了实现变_不可避免的要q反最初的设计构架。经q一D|间以后,软g的架构就千疮癑֭了。bug来多Q越 来越隄护,新的需求越来越隑֮玎ͼ软g的构架对新的需求渐渐的失去支持能力Q而是成ؓ一U制U。最后新需求的开发成本会过开发一个新的Y件的成本Q这 是q个软gpȝ的生命走到尽头的时候?/font>

      重构p够最大限度的避免q样一U现象。系l发展到一定阶D后Q用重构的方式Q不改变pȝ的外部功能,只对内部的结构进行重新的整理。通过重构Q不断的调整pȝ的结构,使系l对于需求的变更始终h较强的适应能力?/font>

  通过重构可以辑ֈ以下的目标:

  ·持箋偏纠和改qY件设?/font>

  重构和设计是相辅相成的,它和设计彼此互补。有了重构,你仍然必d预先的设计,但是不必是最优的设计Q只需要一个合理的解决Ҏ够了,如果没有重构?a target="_new">E序设计会逐渐腐|变质Q愈来愈像断U的风筝Q脱~的野马无法控制。重构其实就是整理代码,让所有带着发散們֐的代码回归本位?/font>

  ·使代码更易ؓ人所理解

  Martin Flower在《重构》中有一句经典的话:"M一个傻瓜都能写?a target="_new">计算?/a>可以理解的程序,只有写出?a target="_new">c?/a>Ҏ理解的程序才是优U的程序员?ҎQ笔者感触很深,有些E序员L能够快速编写出可运行的代码Q但代码中晦涩的命名使h晕眩得需要紧握坐椅扶手,试想一个新兵到来接手这L代码他会不会惛_逃兵呢?

  软g的生命周期往往需要多批程序员来维护,我们往往忽略了这些后来h。ؓ了代码Ҏ被他人理解,需要在实现软g功能时做许多额外的事Ӟ如清晰的排版布局Q简明扼要的注释Q其中命名也是一个重要的斚w。一个很好的办法是采用暗喻命名Q即?a target="_new">对象实现的功能的依据Q用形象化或拟h化的手法q行命名Q一个很好的态度是每个代码元素像新生儿一样命名,也许W者有点命名偏执狂的們֐Q如能荣此雅P深以此为幸?/font>

  对于那些让h充满q感甚臌导性的命名Q需要果军_、大刀阔斧地整容,永远不要手下留情Q?/font>

  ·帮助发现隐藏的代码缺?/font>

  孔子说过Q温故而知新。重构代码时D你加q解原先所写的代码。笔者常有写下程序后Q却发生对自 qE序逻辑不甚理解的情景,曾ؓ此惊悚过Q后来发现这U症状居然是许多E序员常患的"感冒"。当你也发生q样的情形时Q通过重构代码可以加深对原设计? 理解Q发现其中的问题和隐患,构徏出更好的代码?/font>

  ·从长q来看,有助于提高编E效?/font>

  当你发现解决一个问题变得异常复杂时Q往往不是问题本n造成的,而是你用错了ҎQ拙劣的设计往往D臃肿的编码?/font>

  改善设计、提高可L、减缺陷都是ؓ了稳住阵脚。良好的设计是成功的一半,停下来通过重构改进设计Q或怼在当前减~速度Q但它带来的后发优势却是不可低估的?/font>

二、何时着手重构(RefactoringQ?/strong>

  新官上Q三把火,开始一个全新的目ӞE序员往往也会燃v三把火:紧锣密鼓、脚不停y、加班加点,一支声势浩大的千军??夹裹着E序员激情和扣击键盘的鸣金奋力前行,势如破竹Q攻城掠圎ͼ直指"黄龙??/font>

  开发经理是q支浩汤汤代码队伍的统帅,他负责这支队伍的命运Q当齐恒公站在山上看到仲训练? 队伍整齐划一地前q时Q他感叹?我有q样一支军队哪里还怕没有胜利呢Q?。但很遗憾,你手中的q支队伍原本只是散兵游勇Q在前进中招兵买马,不断壮大Q? 所以队伍变形在所隑օ。当开发经理发觉队伍变形时Q也许就是克制住d前方山头的诱惑,停下脚步整顿队伍的时候了?

  Kent Beck提出?代码坏味?的说法,和我们所提出?队伍变Ş"是同L意思,队伍变Ş的信h什么呢Q以下列q的代码症状是"队伍变Ş"的强烈信P

  ·代码中存在重复的代码

  中国?18 家整车生产企业,数量几乎{于、日、欧所有汽车厂家数之和Q但是全国的q量却不及一个外国大汽R公司的量。重复徏讑֏会导致效率的低效和资源的费?/font>

  E序代码更是不能搞重复徏设,如果同一个类中有相同的代码块Q请把它提炼成类的一个独立方法,如果不同cMh相同的代码,h它提炼成一个新c,永远不要重复代码?


·q大的类和过长的Ҏ

  q大的类往往是类抽象不合理的l果Q类抽象不合理将降低了代码的复用率。方法是cȝ国中的诸侯国Q? 怾国太大势必动摇中央集权。过长的Ҏ׃包含的逻辑q于复杂Q错误机率将直线上升Q而可L则直线下降Q类的健壮性很Ҏ被打破。当看到一个过长的? 法时Q需要想办法其划分为多个小ҎQ以便于分而治之?/font>

  ·牵一毛而需要动全n的修?/font>

  当你发现修改一个小功能Q或增加一个小功能Ӟ引发一ơ代码地震,也许是你的设计抽象度不够理想Q功能代码太q分散所引v的?/font>

  ·cM间需要过多的通讯

  Ac需要调用Bcȝq多Ҏ讉KB的内部数据,在关pMq两个类昑־有点狎昵Q可能这两个cL应该在一P而不应该分家?/font>

  ·q度耦合的信息链

  "计算机是q样一门科学,它相信可以通过d一?a target="_new">中间?/a>解决M问题"Q所以往往中间层会被过多地q加到程序中。如果你在代码中看到需要获取一个信息,需要一个类的方法调用另一个类的方法,层层挂接Q就象输油管一栯节相q。这往往是因接层太多造成的,需要查看就否有可移除的中间层,或是否可以提供更直接的调用方法?/font>

  ·各立山头q革?/font>

  如果你发现有两个cL两个Ҏ虽然命名不同但却拥有怼或相同的功能Q你会发现往往是因为开?a target="_new">团队成员协调不够造成的。笔者曾l写了一个颇好用的字W串处理c,但因为没有及旉告团队其他人员Q后来发现项目中居然有三个字W串处理cR革命资源是珍贵的,我们不应各立山头q革命?

  ·不完的设计

  在笔者刚完成的一个比Ҏ警项目中Q曾安排阿朱开发报警模块,即通过Socket向指定的短信q_、语韛_^台及客户?/a>? 警器插g发送报警报文信息,阿朱地完成了q项d。后来用户又提出了实时比对的需求,卌求第三方pȝ以报文Ş式向比对报警pȝ发送请求,比对报警p? l接收ƈ响应q个h。这又需要用到Socket报文通讯Q由于原来的设计没有报文通讯模块独立出来Q所以无法复用阿朱开发的代码。后来我及时调整了这 个设计,新增了一个报文收发模块,使系l所有的对外通讯都复用这个模块,pȝ的整体设计也昑־更加合理?/font>

  每个pȝ都或多或存在不完美的设计,刚开始可能注意不刎ͼ到后来才会慢慢凸昑և来,此时唯有勇于更改才是最好的\?/font>

  ·~少必要的注?/font>

  虽然许多软g工程? 书籍常提醒程序员需要防止过多注释,但这个担心好象ƈ没有什么必要。往往E序员更感兴的是功能实现而非代码注释Q因为前者更能带来成感Q所以代码注? 往往不是q多而是q少Q过于简单。h的记忆曲U下降的坡度是陡得吓人的Q当q了一D|间后再回头补注释Ӟ很容易发?提笔忘字Q愈a且止"的情形?/font>

  曑֜|上看到q微软的代码注释Q其详尽E度让h叹ؓ观止Q也从中体悟C微Y成功的一个经验?

三、重构(RefactoringQ的N

      学习一U可以大q提高生产力的新技术时Q你L难以察觉其不适用的场合。通常你在一个特定场景中学习它,q个场景往往是个目。这U情况下你很隄Z么会造成q种新技术成效不彰或甚至形成危害。十q前Q对象技术(object tech.Q的情况也是如此。那时如果有人问我「何时不要用对象」,我很隑֛{。ƈ非我认ؓ对象十全十美、没有局限??我最反对q种盲目态度Q而是管我知道它的好处,但确实不知道其局限性在哪儿?/font>

      现在Q重构的处境也是如此。我们知道重构的好处Q我们知道重构可以给我们的工作带来垂手可得的改变。但是我们还没有获得_的经验,我们q看不到它的局限性?br /> q? 一节比我希望的要短。暂且如此吧。随着更多人学会重构技巧,我们也将对它有更多了解。对你而言q意呻I虽然我坚册Z应该试一下重构,获得它所提供 的利益,但在此同Ӟ你也应该时时监控其过E,注意L重构可能引入的问题。请让我们知道你所遭遇的问题。随着寚w构的了解日益增多Q我们将扑և更多解决 办法Qƈ清楚知道哪些问题是真正难以解决的?/font>

      数据?/a>QDatabasesQ?/font>

      「重构」经常出问题的一个领域就是数据库。绝大多数商用程序都与它们背后的database schemaQ数据库表格l构Q紧密耦合QcoupledQ在一Pq也是database schema如此难以修改的原因之一。另一个原因是数据q移QmigrationQ。就你非常心地将pȝ分层QlayeredQ,database schema和对象模型(object modelQ间的依赖降x低,但database schema的改变还是让你不得不q移所有数据,q可能是件O长而烦琐的工作?/font>

      在「非对象数据库」(nonobject databasesQ中Q解册个问题的办法之一是Q在对象模型Qobject modelQ和数据库模型(database modelQ之间插入一个分隔层Qseparate layerQ,q就可以隔离两个模型各自的变化。升U某一模型时无需同时升另一模型Q只需升上述的分隔层卛_。这L分隔层会增加pȝ复杂度,但可? l你很大的灵zd。如果你同时拥有多个数据库,或如果数据库模型较ؓ复杂使你难以控制Q那么即使不q行重构Q这分隔层也是很重要的?/font>

      你无需一开始就插入分隔层,可以在发现对象模型变得不E_时再产生它。这样你可以ؓ你的改变扑ֈ最好的杠杆效应?/font>

      对开发者而言Q对象数据库既有帮助也有妨碍。某?a target="_new">面向对象? 据库提供不同版本的对象之间的自动q移功能Q这减少了数据迁UL的工作量Q但q是会损׃定时间。如果各数据库之间的数据q移q自动q行Q你必自? 完成q移工作Q这个工作量可是很大的。这U情况下你必L加留classes内的数据l构变化。你仍然可以攑ֿclasses的行{U过去,但{U? 值域QfieldQ时必L外小心。数据尚未被转移前你得先运用访问函敎ͼaccessorsQ造成「数据已l{UR的假象。一旦你定知道「数据应 该在何处」时Q就可以一ơ性地数据迁U过厅R这时惟一需要修改的只有讉K函数QaccessorsQ,q也降低了错误风险?/font>

      修改接口QChanging InterfacesQ?/font>

      关于对象Q另一仉要事情是Q它们允怽分开修改软g模块的实玎ͼimplementationQ和接口QinterfaceQ。你可以安全CҎ对象内部而不影响他hQ但对于接口要特别}??如果接口被修改了QQ何事情都有可能发生?/font>

      一直对重构带来困扰的一件事是Q许多重构手法的会修改接口。像Rename MethodQ?73Q这么简单的重构手法所做的一切就是修Ҏ口。这Ҏ为珍늚装概念会带来什么媄响呢Q?/font>

      如果某个函数的所有调用动作都在你的控制之下,那么即修改函数名称也不会有M问题。哪怕面对一个public函数Q只要能取得q修改其所有调用者,? 也可以安心地这个函数易名。只有当需要修改的接口p被那些「找不到Q即使找C不能修改」的代码使用Ӟ接口的修Ҏ会成为问题。如果情늜是如此,? ׃_q个接口是个「已发布接口」(published interfaceQ?比公开接口Qpublic interfaceQ更q一步。接口一旦发行,你就再也无法仅仅修改调用者而能够安全地修改接口了。你需要一个略为复杂的E序?/font>

      q个x改变了我们的问题。如今的问题是:该如何面寚w些必M攏V已发布接口」的重构手法Q?/font>

      a之,如果重构手法改变了已发布接口Qpublished interfaceQ,你必d时维护新旧两个接口,直到你的所有用户都有时间对q个变化做出反应。幸q的是这不太困难。你通常都有办法把事情组l好Q让 旧接口l工作。请量q么做:让旧接口调用新接口。当你要修改某个函数名称ӞL下旧函数Q让它调用新函数。千万不要拷贝函数实现码Q那会让你陷? 「重复代码」(duplicated codeQ的泥淖中难以自拔。你q应该用Java提供?deprecationQ反对)设施Q将旧接口标Cؓ "deprecated"。这么一来你的调用者就会注意到它了?/font>

      q个q程的一个好例子是Java容器c(collection classesQ。Java 2的新容器取代了原先一些容器。当Java 2容器发布ӞJavaSoft׃很大力气来ؓ开发者提供一条顺利迁徙之路?/font>

      「保留旧接口」的办法通常可行Q但很烦人。v码在一D|间里你必d造(buildQƈl护一些额外的函数。它们会使接口变得复杂,使接口难以用。还? 我们有另一个选择Q不要发布(publishQ接口。当然我不是说要完全止Q因为很明显你必得发布一些接口。如果你正在建造供外部使用的APIsQ像 Sun所做的那样Q肯定你必得发布接口。我之所以说量不要发布Q是因ؓ我常常看C些开发团队公开了太多接口。我曄看到一支三人团队这么工作:每个? 都向另外两h公开发布接口。这使他们不得不l常来回l护接口Q而其实他们原本可以直接进入程序库Q径行修改自q理的那一部分Q那会轻松许多。过度强? 「代码拥有权」的团队常常会犯q种错误。发布接口很有用Q但也有代h。所以除非真有必要,别发布接口。这可能意味需要改变你的代码拥有权观念Q让每个人都 可以修改别h的代码,以运应接口的改动。以搭档Q成对)~程QPair ProgrammingQ完成这一切通常是个好主意?/font>

      不要q早发布QpublishedQ接口。请修改你的代码拥有权政{,佉K构更畅?/font>

      Java之中q有一个特别关于「修Ҏ口」的问题Q在throws子句中增加一个异常。这q不是对{֐式(signatureQ的修改Q所以你无法? delegationQ委托手法)隐藏它。但如果用户代码不作出相应修改,~译器不会让它通过。这个问题很难解冟뀂你可以个函数选择一个新名字Q让? 函数调用它,q将q个新增的checked exceptionQ可控式异常Q{换成一个unchecked exceptionQ不可控异常Q。你也可以拋Z个unchecked异常Q不q这样你׃失去验能力。如果你那么做,你可以警告调用者:q个 unchecked异常日后会变成一个checked异常。这样他们就有时间在自己的代码中加上Ҏ异常的处理。出于这个原因,我L喜欢为整? package定义一个superclass异常Q就像java.sql的SQLExceptionQ,q确保所有public函数只在自己的throws子句中声明这个异常。这h可以随心所Ʋ地定义subclass异常Q不会媄响调用者,因ؓ调用者永q只知道那个更具一般性的superclass异常?/font>

      难以通过重构手法完成的设计改?/font>

      通过重构Q可以排除所有设计错误吗Q是否存在某些核心设计决{,无法以重构手法修改?在这个领域里Q我们的l计数据不完整。当然某些情况下我们可以很有 效地重构Q这常常令我们倍感惊讶Q但的确也有难以重构的地斏V比如说在一个项目中Q我们很难(但还是有可能Q将「无安全需求(no security requirementsQ情况下构造v来的pȝ」重构ؓ「安全性良好的Qgood securityQ系l」?/font>

      q种情况下我的办法就是「先惌重构的情c。考虑候选设计方案时Q我会问自己Q将某个设计重构为另一个设计的隑ֺ有多大?如果看上d单,我就不必? 担心选择是否得当Q于是我׃选最单的设计Q哪怕它不能覆盖所有潜在需求也没关pR但如果预先看不到简单的重构办法Q我׃在设计上投入更多力气。不q? 我发玎ͼq种情况很少出现?/font>

      何时不该重构Q?/font>

      有时候你Ҏ不应该重??例如当你应该重新~写所有代码的时候。有时候既有代码实在太混ؕQ重构它q不如从新写一个来得简单。作U决定很困难Q我承认我也没有什么好准则可以判断何时应该攑ּ重构?/font>

      重写Q而非重构Q的一个清楚讯号就是:现有代码Ҏ不能正常q作。你可能只是试着做点试Q然后就发现代码中满是错误,Ҏ无法E_q作。记住,重构之前Q代码必v码能够在大部分情况下正常q作?/font>

      一个折衷办法就是:「大块头软g」重构ؓ「封装良好的型lg」。然后你可以逐一对组件作出「重构或重徏」的军_。这是一个颇具希望的办法Q但我还没有_数据Q所以也无法写出优秀的指导原则。对于一个重要的古老系l,q肯定会是一个很好的方向?/font>

      另外Q如果项目已q最后期限,你也应该避免重构。在此时机,从重构过E赢得的生力只有在最后期限过后才能体现出来,而那个时候已l时不我予。Ward CunninghamҎ有一个很好的看法。他把未完成的重构工作Ş容ؓ「债务」。很多公叔R需要借债来使自己更有效地运转。但是借债就得付利息Q过于复 杂的代码所造成的「维护和扩展的额外开销」就是利息。你可以承受一定程度的利息Q但如果利息太高你就会被压垮。把债务理好是很重要的Q你应该随时通过? 构来偿还一部分债务?/font>

      如果目已经非常接近最后期限,你不应该再分心于重构Q因为已l没有时间了。不q多个项目经验显C:重构的确能够提高生力。如果最后你没有_旉Q通常pCZ其实早该q行重构?/font>

四、重构(RefactoringQ与设计

      「重构」肩负一特别Q务:它和设计彼此互补。初学编E的时候,我埋头就写程序,浑噩噩地进行开发。然而很快我便发玎ͼ「事先设计」(upfront designQ可以助我节省回头工的高昂成本。于是我很快加强q种「预先设计」风根{许多h都把设计看作软g开?/a>? 关键环节Q而把~程QprogrammingQ看作只是机械式的低U劳动。他们认计就像画工程图而编码就像施工。但是你要知道,软g和真实器械有着? 大的差异。Y件的可塑性更强,而且完全是思想产品。正如Alistair Cockburn所_『有了设计,我可以思考更快,但是其中充满漏z。?/font>

      有一U观点认为:重构可以成ؓ「预先设计」的替代品。这意思是你根本不必做M设计Q只按照最初想法开始编码,让代码有效运作,然后再将它重构成型。事实上q种办法真的可行。我的确看过有hq么做,最后获得设计良好的软g?a target="_new">极限~程Q?a target="_new">Extreme ProgrammingQ[Beck, XP] 的支持者极力提倡这U办法?/font>

      管如上所aQ只q用重构也能收到效果Q但qƈ不是最有效的途径。是的,即极限~程QExtreme ProgrammingQ爱好者也会进行预先设计。他们会使用CRC卡或cM的东西来验各U不同想法,然后才得到第一个可被接受的解决ҎQ然后才能开 始编码,然后才能重构。关键在于:重构改变了「预先设计」的角色。如果没有重构,你就必须保证「预先设计」正无误,q个压力太大了。这意味如果来需? 对原始设计做M修改Q代价都非帔R昂。因此你需要把更多旉和精力放在预先设计上Q以避免日后修改?/font>

      如果你选择重构Q问题的重点p{变了。你仍然做预先设计,但是不必一定找出正的解决Ҏ。此ȝ你只需要得C个够合理的解决Ҏ够了。你很肯定地 知道Q在实现q个初始解决Ҏ的时候,你对问题的理解也会逐渐加深Q你可能会察觉最佌x案和你当初设想的有些不同。只要有重构q项武器在手Q就不成? 题,因ؓ重构让日后的修改成本不再高昂?/font>

      q种转变D一个重要结果:软g设计朝向化前q了一大步。过L曾运用重构时Q我L力求得到灉|的解x案。Q何一个需求都让我提心吊胆地猜疑:在系 l寿命期_q个需求会D怎样的变化?׃变更设计的代价非帔R昂,所以我希望建造一个够灵zR够强固的解决ҎQ希望它能承受我所能预见的所有需 求变化。问题在于:要徏造一个灵zȝ解决ҎQ所需的成本难以估。灵zȝ解决Ҏ比简单的解决Ҏ复杂许多Q所以最l得到的软g通常也会更难l护 ? 虽然它在我预先设想的方向上的是更加灉|。就幸q走在预先设想的方向上,你也必须理解如何修改设计。如果变化只出现在一两个地方Q那不算大问题。然? 变化其实可能出现在系l各处。如果在所有可能的变化出现地点都徏立v灉|性,整个pȝ的复杂度和维护难度都会大大提高。当Ӟ如果最后发现所有这些灵zL? 都毫无必要,q才是最大的p|。你知道Q这其中肯定有些灉|性的派不上用场Q但你却无法预测到底是哪些派不上用场。ؓ了获得自己想要的灉|性,你不得不 加入比实际需要更多的灉|性?/font>

      有了重构Q你可以通过一条不同的途径来应付变化带来的风险。你仍旧需要思考潜在的变化Q仍旧需要考虑灉|的解x案。但是你不必再逐一实现q些解决? 案,而是应该问问自己Q『把一个简单的解决Ҏ重构成这个灵zȝҎ有多大难度?』如果答案是「相当容易」(大多数时候都如此Q,那么你就只需实现目前? 单方案就行了?br /> 重构可以带来更简单的设计Q同时又不损qzL,q也降低了设计过E的隑ֺQ减M设计压力。一旦对重构带来的简单性有更多? 受,你甚臛_以不必再预先思考前q所谓的灉|Ҏ ? 一旦需要它Q你L_的信心去重构。是的,当下只管建造可q行的最化系l,至于灉|而复杂的设计Q唔Q多数时候你都不会需要它?/font>

      劌无获?Ron Jeffries

      Chrysler Comprehensive CompensationQ克莱斯勒综合薪资系l)的支付过E太慢了。虽然我们的开发还没结束,q个问题却已l开始困扰我们,因ؓ它已l拖累了试速度?/font>

      Kent Beck、Martin Fowler和我军_解决q个问题。等待大伙儿会合的时间里Q凭着我对q个pȝ的全盘了解,我开始推:到底是什么让pȝ变慢了?我想到数U可能,然后? 伙伴们谈了几U可能的修改Ҏ。最后,关于「如何让q个pȝq行更快」,我们提出了一些真正的好点子?/font>

      然后Q我们拿Kent的量工?a target="_new">度量了系l性能。我一开始所想的可能性竟然全都不是问题肇因。我们发玎ͼpȝ把一半时间用来创建「日期」实体(instanceQ。更有趣的是Q所有这些实体都有相同的倹{?/font>

      于是我们观察日期的创建逻辑Q发现有Z它优化。日期原本是由字W串转换而生Q即使无外部输入也是如此。之所以用字W串转换方式Q完全是Z方便键盘输入。好Q也许我们可以将它优化?/font>

      于是我们观察日期怎样被这个程序运用。我们发玎ͼ很多日期对象都被用来产生「日期区间」实体(instanceQ。「日期区间」是个对象,׃个v始日期和一个结束日期组成。仔l追t下去,我们发现l大多数日期区间是空的!

      处理日期区间时我们遵循这样一个规则:如果l束日期在v始日期之前,q个日期区间p是空的。这是一条很好的规则Q完全符合这?a target="_new">class的需要。采用此一规则后不久,我们意识刎ͼ创徏一个「v始日期在l束日期之后」的日期区间Q仍然不是清晰的代码,于是我们把这个行为提炼到一个factory methodQ译注:一个著名的设计模式Q见?a target="_new">Design Patterns》)Q由它专门创建「空的日期区间」?/font>

      我们做了上述修改Q代码更加清晰Q却意外得到了一个惊喜。我们创Z个固定不变的「空日期区间」对象,q让上述调整后的factory method每次都返回该对象Q而不再每ơ都创徏新对象。这一修改把系l速度提升了几乎一倍,以让测试速度辑ֈ可接受程度。这只花了我们大U五分钟?/font>

      我和团队成员QKent和Martin谢绝参加Q认真推过Q我们了若指掌的q个E序中可能有什么错误?我们甚至凭空做了些改q设计,却没有先对系l的真实情况q行量测?/font>

      我们完全错了。除了一场很有趣的交谈,我们什么好事都没做?/font>

      教训Q哪怕你完全了解pȝQ也请实际量它的性能Q不要臆。臆会让你学到一些东西,但十有八九你是错的?/font>

五、重构与性能QPerformanceQ?/strong>

      译注Q在我的接触l验中,performance一词被不同的h予以不同的解释和认知Q效率、性能、效能。不同地区(例如台湾和大陆)的习惯用法亦不相 同。本书一遇performance我便译ؓ性能。efficient译ؓ高效Qeffective译ؓ有效?/font>

      关于重构Q有一个常被提出的问题Q它对程序的性能造成怎样的媄响?Z让Y件易于理解,你常会作Z些ɽE序q行变慢的修攏V这是个重要的问题。我q不 赞成Z提高设计的纯z性或把希望寄托于更快的硬件n上,而忽略了E序性能。已l有很多软g因ؓ速度太慢而被用户拒绝Q日益提高的机器速度亦只不过略微? 宽了速度斚w的限制而已。但是,换个角度_虽然重构必然会软gq行更慢Q但它也使Y件的性能优化更易q行。除了对性能有严D求的实时Qreal timeQ系l,其它M情况下「编写快速Y件」的U密是Q首先写出可调(tunableQYӞ然后调整它以求获得够速度?/font>

      我看q三U「编写快速Y件」的Ҏ。其中最严格的是「时间预法」(time budgetingQ,q通常只用于性能要求极高的实时系l。如果用这U方法,分解你的设计时就要做好预,l每个组仉先分配一定资?? 包括旉和执行轨q(footprintQ。每个组件绝对不能超q预算Q就拥有「可在不同组件之间调度预配时间」的机制也不行。这U方法高度重? 性能Q对于心律调节器一cȝpȝ是必ȝQ因为在q样的系l中q来的数据就是错误的数据。但对其他类pȝQ例如我l常开发的企业信息pȝQ而言Q如此追求高性能有点过份了?/font>

      W二U方法是「持l关切法」(constant attentionQ。这U方法要求Q何程序员在Q何时间做M事时Q都要设法保持系l的高性能。这U方式很常见Q感觉上很有吸引力,但通常不会起太大作 用。Q何修改如果是Z提高性能Q通常会ɽE序难以l护Q因而减~开发速度。如果最l得到的软g的确更快了,那么q点损失有所|可惜通常事与愿违Q因 为性能改善一旦被分散到程序各角落Q每ơ改善都只不q是从「对E序行ؓ的一个狭隘视角」出发而已?/font>

      关于性能Q一件很有趣的事情是Q如果你对大多数E序q行分析Q你会发现它把大半时间都耗费在一半代码w上。如果你一视同仁地优化所有代码,90% 的优化工作都是白费劲儿,因ؓ被你优化的代码有许多隑־被执行v来。你花时间做优化是ؓ了让E序q行更快Q但如果因ؓ~Z对程序的清楚认识而花Ҏ_? 些时间都是被费掉了?/font>

       W三U性能提升法系利用上述?"90%" l计数据。采用这U方法时Q你以一U「良好的分解方式」(well-factored mannerQ来建造自qE序Q不Ҏ能投以M兛_Q直臌入性能优化阶段 ? 那通常是在开发后期。一旦进入该阶段Q你再按照某个特定程序来调整E序性能?/font>

     在性能优化阶段中,你首先应该以一个量工L控程序的q行Q让它告诉你E序中哪些地方大量消耗时间和I间。这样你可以找出性能热点Qhot spotQ所在的一段代码。然后你应该集中兛_q些性能热点Qƈ使用前述「持l关切法」中的优化手D|优化它们。由于你把注意力都集中在热点上,较少? 工作量便可显现较好的成果。即便如此你q是必须保持谨慎。和重构一P你应该小q度q行修改。每C步都需要编译、测试、再ơ量。如果没能提高性能Q就 应该撤销此次修改。你应该l箋q个「发现热炏V去除热炏V的q程Q直到获得客h意的性能为止。关于这Ҏ术,McConnell [McConnell] 为我们提供了更多信息?/font>

      一个被良好分解Qwell-factoredQ的E序可从两方面帮助此U优化Ş式。首先,它让你有比较充裕的时间进行性能调整Qperformance tuningQ,因ؓ有分解良好的代码在手Q你p够更快速地d功能Q也有更多旉用在性能问题上(准确的量则保证你把q些旉投资在恰当地点)? 其次Q面对分解良好的E序Q你在进行性能分析时便有较l的_度QgranularityQ,于是量测工具把你带入范围较小的程序段落中Q而性能的调整也? 较容易些。由于代码更加清晎ͼ因此你能够更好地理解自己的选择Q更清楚哪种调整起关键作用?/font>

      我发现重构可以帮助我写出更快的Y件。短E看来,重构的确会软g变慢Q但它优化阶段中的软g性能调整更容易。最l我q是有赚头?/font>

六、重构v源何处?

      我曾l努力想扑և重构QrefactoringQ一词的真正hQ但最l失败了。优UE序员肯定至会׃些时间来清理自己的代码。这么做是因为,他们知道z的代码比杂乱无章的代码更容易修改,而且他们知道自己几乎无法一开始就写出z的代码?/font>

      重构不止如此。本书中我把重构看作整个软g开发过E的一个关键环节。最早认识重构重要性的两个人是Ward Cunningham和Kent BeckQ他们早?980s之前开始用SmalltalkQ那是个特别适合重构的环境。Smalltalk是一个十分动态的环境Q你可以很快写出? 具功能的软g。Smalltalk的「编?q结/执行」周期非常短Q因此很Ҏ快速修改代码。它是面向对象,所以也能够提供强大工具Q最大限度地修? 的媄响隐藏于定义良好的接口背后。Ward和Kent努力发展Z套适合q类环境的Y件开发过E(如今Kent把这U风格叫作极限编E?[Beck, XP]Q。他们意识到Q重构对于提高他们的生力非帔R要。从那时起他们就一直在工作中运用重构技术,在严肃而认真的软g目中用它Qƈ不断_q个E? 序?/font>

      Ward和Kent的思想对SmalltalkC产生了极大媄响,重构概念也成为Smalltalk文化中的一个重要元素。SmalltalkC的另 一位领袖是Ralph JohnsonQ伊利诺斯大学乌班U_校教授,著名的「四巨头?[Gang of Four] 之一。Ralph最大的兴趣之一是开发Y?a target="_new">框架Q?a target="_new">frameworkQ。他揭示了重构对于灵z高效框架的开发帮助?/font>

     Bill Opdyke是Ralph的博士研I生Q对框架也很感兴。他看到重构的潜在h|q看到重构应用于Smalltalk之外的其它语a的可能性。他的技? 背景是电话交换系l的开发。在q种pȝ中,大量的复杂情况与时俱增,而且非常难以修改。Bill的博士研I就是从工具构筑者的角度来看待重构。通过研究Q? Bill发现Q在C++ framework开发项目中Q重构很有用。他也研I了极有必要的「语义保持性(semantics-preservingQ重构」及其证明方式,以及? 何以工具实现重构。时至今日,Bill的博士论?[Opdyke] 仍然是重构领域中最有h倹{最丰硕的研I成果。此外他为本书撰写了W?3章?/font>

      我还记得1992qOOPSLA大会上见到Bill的情景。我们坐在一间咖啡厅里,讨论当时我正Z健业务构{的一个概忉|Ӟconceptual frameworkQ中的某些工作。Bill跟我谈v他的研究成果Q我q记得自己当时的xQ『有,但ƈ非真的那么重要』。唉Q我完全错了?/font>

      John Brant和Don Roberts重构中的「工兗构惛_扬光大,开发了一个名为「重构浏览器」(Refactoring BrowserQ的Smalltalk重构工具。他们撰写了本书W?4章,其中寚w构工具做了更多介l?/font>

      那么Q我呢?我一直有清理代码的們֐Q但从来没有惛_q会有那么重要。后来我和Kent一起做了个目Q看C使用重构手法Q也看到重构对生产性能和? 质量带来的媄响。这份体验让我相信:重构是一门非帔R要的技术。但是,在重构的学习和推q过E中我遇C挫折Q因为我拿不ZQ何一本书l程序员看,也没? M一位专家打写样一本书。所以,在这些专家的帮助下,我写下了q本书?/font>

      优化一个薪资系l?Rich Garzaniti
      Chrysler Comprehensive CompensationQ克莱斯勒综合薪资系l)交给GemStone公司之前Q我们用了相当长的时间开发它。开发过E中我们无可避免地发现程序不? 快,于是找了Jim Haungs ?GemSmith中的一位好??请他帮我们优化这个系l?/font>

      Jim先用一Ҏ间让他的团队了解pȝq作方式Q然后以GemStone的ProfMonitorҎ编写出一个性能量测工具Q将它插入我们的功能试中。这个工具可以显C系l生的对象数量Q以及这些对象的诞生炏V?/font>

      令我们吃惊的是:创徏量最大的对象竟是字符丌Ӏ其中最大的工作量则是反复?2,000-bytes的字W串。这很特别,因ؓq字W串实在太大了,q? GemStone惯用的垃圑֛收设施都无法处理它。由于它是如此巨大,每当被创建出来,GemStone都会它分页QpagingQ至盘上。也是? 字符串的创徏竟然用上了I/O子系l?/a>Q译注:分页机制会动用I/OQ,而每ơ输录时都要产生q样的字W串三次H?/font>

      我们的第一个解军_法是把一?2,000-bytes字符串缓存(cachedQv来,q可解决一大半问题。后来我们又加以修改Q将它直接写入一个file streamQ从而避免生字W串?/font>

      解决了「巨大字W串」问题后QJim的量工具又发现了一些类似问题,只不q字W串E微一些:800-bytes?00-bytes……{等Q我们也都对它们改用file streamQ于是问题都解决了?/font>

      使用q些技术,我们Ex提高了系l性能。开发过E中原本g需?,000时以上才能完成的薪资计,实际q作时只?0时。一个月后我们把旉~短?8时。正式投入运转时只花12时。经q一q的q行和改善后Q全部计只需9时?/font>

      我们的最大改q就是:程序放在多处理器(multi-processorQ计器上,以多U程Qmultiple threadsQ方式运行。最初这个系lƈ非按照多U程思维来设计,但由于代码有良好分解Qwell factoredQ,所以我们只׃天时间就让它得以同时q行多个U程了。现在,薪资的计只需2时?/font>

      在Jim提供工具使我们得以在实际操作中量度系l性能之前Q我们也猜测q问题所在。但如果只靠猜测Q我们需要很长的旉才能试出真正的解法。真实的量测指出了一个完全不同的方向Qƈ大大加快了我们的q度?br />




seal 2008-04-10 23:00 发表评论
]]>
վ֩ģ壺 | | | | | | ԰| | ̩| ۰| Ԫ| | ԫ| | | ͺ| | | | | Ž| | | Ĭ| | | ³ľ| | | | Ӫ| | ٺ| | ͨ| | ʯʨ| | ԣ| | |