没有合适的资源?快使用搜索试试~ 我知道了~
OO设计五个原则.pdf
4星 · 超过85%的资源 需积分: 12 28 下载量 41 浏览量
2007-06-10
18:00:37
上传
评论
收藏 217KB PDF 举报
温馨提示
试读
62页
OO设计五个原则.pdf
资源推荐
资源详情
资源评论
1
The Open-Closed Principle
This is the first of my
Engineering Notebook
columns for
The C++ Report
. The articles
that will appear in this column will focus on the use of C++ and OOD, and will address
issues of software engineering. I will strive for articles that are pragmatic and directly use-
ful to the software engineer in the trenches. In these articles I will make use of Booch’s
notation for documenting object oriented designs. The sidebar provides a brief lexicon of
Booch’s notation.
There are many heuristics associated with object oriented design. For example, “all
member variables should be private”, or “global variables should be avoided”, or “using
run time type identification (RTTI) is dangerous”. What is the source of these heuristics?
What makes them true? Are they
always
true? This column investigates the design princi-
ple that underlies these heuristics -- the open-closed principle.
As Ivar Jacobson said: “All systems change during their life cycles. This must be
borne in mind when developing systems expected to last longer than the first version.”
1
How can we create designs that are stable in the face of change and that will last longer
than the first version? Bertrand Meyer
2
gave us guidance as long ago as 1988 when he
coined the now famous open-closed principle. To paraphrase him:
S
OFTWARE
ENTITIES
(
CLASSES
,
MODULES
,
FUNCTIONS
,
ETC
.)
SHOULD
BE
OPEN
FOR
EXTENSION
,
BUT
CLOSED
FOR
MODIFICATION
.
When a single change to a program results in a cascade of changes to dependent modules,
that program exhibits the undesirable attributes that we have come to associate with “bad”
design. The program becomes fragile, rigid, unpredictable and unreusable. The open-
closed principle attacks this in a very straightforward way. It says that you should design
modules that
never change
. When requirements change, you extend the behavior of such
modules by adding new code, not by changing old code that already works.
Description
Modules that conform to the open-closed principle have two primary attributes.
1. Object Oriented Software Engineering a Use Case Driven Approach, Ivar Jacobson, Addison
Wesley, 1992, p 21.
2. Object Oriented Software Construction, Bertrand Meyer, Prentice Hall, 1988, p 23
2
Description
1. They are “Open For Extension”.
This means that the behavior of the module can be extended. That we can make
the module behave in new and different ways as the requirements of the applica-
tion change, or to meet the needs of new applications.
2. They are “Closed for Modification”.
The source code of such a module is inviolate. No one is allowed to make source
code changes to it.
It would seem that these two attributes are at odds with each other. The normal way to
extend the behavior of a module is to make changes to that module. A module that cannot
be changed is normally thought to have a fixed behavior. How can these two opposing
attributes be resolved?
Abstraction is the Key.
In C++, using the principles of object oriented design, it is possible to create abstractions
that are fixed and yet represent an unbounded group of possible behaviors. The abstrac-
tions are abstract base classes, and the unbounded group of possible behaviors is repre-
sented by all the possible derivative classes. It is possible for a module to manipulate an
abstraction. Such a module can be closed for modification since it depends upon an
abstraction that is fixed. Yet the behavior of that module can be extended by creating new
derivatives of the abstraction.
Figure 1 shows a simple design that does not conform to the open-closed principle.
Both the
Client
and
Server
classes are concrete. There is no guarantee that the mem-
ber functions of the
Server
class are virtual. The
Client
class
uses
the
Server
class.
If we wish for a
Client
object to use a different server object, then the
Client
class
must be changed to name the new server class.
Figure 2 shows the corresponding design that conforms to the open-closed principle.
In this case, the
AbstractServer
class is an abstract class with pure-virtual member
functions. the
Client
class uses this abstraction. However objects of the
Client
class
will be using objects of the derivative
Server
class. If we want
Client
objects to use a
different server class, then a new derivative of the
AbstractServer
class can be cre-
ated. The
Client
class can remain unchanged.
Figure 1
Closed Client
Client
Server
3
: The Open-Closed Principle
The
Shape
Abstraction
Consider the following example. We have an application that must be able to draw circles
and squares on a standard GUI. The circles and squares must be drawn in a particular
order. A list of the circles and squares will be created in the appropriate order and the pro-
gram must walk the list in that order and draw each circle or square.
In C, using procedural techniques that do not conform to the open-closed principle,
we might solve this problem as shown in Listing 1. Here we see a set of data structures
that have the same first element, but are different beyond that. The first element of each is
a type code that identifies the data structure as either a circle or a square. The function
DrawAllShapes
walks an array of pointers to these data structures, examining the type
code and then calling the appropriate function (either
DrawCircle
or
DrawSquare
).
Figure 2
Open Client
Listing 1
Procedural Solution to the Square/Circle Problem
enum ShapeType {circle, square};
struct Shape
{
ShapeType itsType;
};
struct Circle
{
ShapeType itsType;
double itsRadius;
Point itsCenter;
};
Client
Abstract
Server
Server
A
4
The Shape Abstraction
The function
DrawAllShapes
does not conform to the open-closed principle
because it cannot be closed against new kinds of shapes. If I wanted to extend this function
to be able to draw a list of shapes that included triangles, I would have to modify the func-
tion. In fact, I would have to modify the function for any new type of shape that I needed
to draw.
Of course this program is only a simple example. In real life the
switch
statement in
the
DrawAllShapes
function would be repeated over and over again in various func-
tions all over the application; each one doing something a little different. Adding a new
shape to such an application means hunting for every place that such
switch
statements
(or
if/else
chains) exist, and adding the new shape to each. Moreover, it is very
unlikely that all the
switch
statements and
if/else
chains would be as nicely struc-
tured as the one in
DrawAllShapes
. It is much more likely that the predicates of the
if
struct Square
{
ShapeType itsType;
double itsSide;
Point itsTopLeft;
};
//
// These functions are implemented elsewhere
//
void DrawSquare(struct Square*)
void DrawCircle(struct Circle*);
typedef struct Shape *ShapePointer;
void DrawAllShapes(ShapePointer list[], int n)
{
int i;
for (i=0; i<n; i++)
{
struct Shape* s = list[i];
switch (s->itsType)
{
case square:
DrawSquare((struct Square*)s);
break;
case circle:
DrawCircle((struct Circle*)s);
break;
}
}
}
Listing 1 (Continued)
Procedural Solution to the Square/Circle Problem
5
: The Open-Closed Principle
statements would be combined with logical operators, or that the
case
clauses of the
switch
statements would be combined so as to “simplify” the local decision making.
Thus the problem of finding and understanding all the places where the new shape needs
to be added can be non-trivial.
Listing 2 shows the code for a solution to the square/circle problem that conforms to
the open-closed principle. In this case an abstract
Shape
class is created. This abstract
class has a single pure-virtual function called
Draw
. Both
Circle
and
Square
are
derivatives of the
Shape
class.
Note that if we want to extend the behavior of the
DrawAllShapes
function in
Listing 2 to draw a new kind of shape, all we need do is add a new derivative of the
Shape
class. The
DrawAllShapes
function does not need to change. Thus
DrawAllShapes
conforms to the open-closed principle. Its behavior can be extended
without modifying it.
In the real world the
Shape
class would have many more methods. Yet adding a new
shape to the application is still quite simple since all that is required is to create the new
derivative and implement all its functions. There is no need to hunt through all of the
application looking for places that require changes.
Since programs that conform to the open-closed principle are changed by adding new
code, rather than by changing existing code, they do not experience the cascade of changes
exhibited by non-conforming programs.
Listing 2
OOD solution to Square/Circle problem.
class Shape
{
public:
virtual void Draw() const = 0;
};
class Square : public Shape
{
public:
virtual void Draw() const;
};
class Circle : public Shape
{
public:
virtual void Draw() const;
};
void DrawAllShapes(Set<Shape*>& list)
{
for (Iterator<Shape*>i(list); i; i++)
(*i)->Draw();
}
剩余61页未读,继续阅读
资源评论
- kingofthe2011-10-28还可以吧,好多我都不能用
daduxiaoyu
- 粉丝: 0
- 资源: 5
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功