Form Template Method - You have two methods in ...

toaststicksrifleΛογισμικό & κατασκευή λογ/κού

15 Αυγ 2012 (πριν από 5 χρόνια και 2 μήνες)

350 εμφανίσεις

Piotr Warczak

CSS 555


Spring 2010


Refactoring
As a

M
eans
T
o
I
mprove

S
oftware
Q
uality



Definition

Refactoring is the process of transforming the internal structure of existing code while keeping the
integrity of the code’s functional requirements. Refa
ctoring is proven to increase program
maintainability, flexibility, and understandability and is recognized as a best practice in the software
development community
[1, 9].


History

Refactoring comes from mathematics when an expression is factored into i
ts equivalence


the
factors are cleaner ways of expressing the same statement.


The first person to use refactoring
phrase
in print was Ralph Johnson, but it’s
believed

that he most
likely
learned about it
from Smalltalk programmers at Tektronix. He and
Bill Opdyke
wrote a paper
about refactoring in 1990 with emphasis on the importance of refactoring in the design of frameworks.
Then Bill Opdyke
wrote a PhD thesis on refactoring in 1992.

Furthermore,
John Brant and Don Roberts
built the Refactoring Brow
s
er for Smalltalk, and Don wrote a PhD thesis on it in 1999.



Why Refactor?

Fowler’s catalogue of refactoring [
9
] can be considered to be the work which triggered industry
adoption of refactoring practices. In his book, Fowler suggests four purposes of re
factoring:




Improve the design of software



Through accumulating code changes, code loses its structure,
thereby increasingly drifting towards a state of decay. Refactoring can be used to cure software
decay by redistributing parts of the code to the righ
t places, and by removing duplicated code.
The claim that refactoring can improve the design of software is confirmed by [
15
] with regard
to cohesion and by [
11
] with regards to coupling, as indicators for internal software quality.
Another claimed benefit

in the area of improved design is improved flexibility [
4
].




Make software easier to understand



Refactoring can help make the code more readable by
making it better communicate its purpose. A different way in which refactoring supports
program understan
ding is in reflecting hypotheses about the purpose of the code by changing
the code, and afterwards testing that understanding through rerunning the code. The
suggested process to do so is to start refactoring the little details to clarify the code, there
by
exposing the design. The potential to improve understandability through refactoring is
confirmed by many authors [
4,

11,

15,

16
]. In more specific terms, [
8
] discusses how refactorings
can be used to improve communicating the purpose of the code.




Help

find bugs



Through clarifying the structure of the code, the assumptions within the code
are also clarified, making it easier to find bugs.




Program faster



Through improving the design and overall understandability of the code, rapid
software developme
nt is supported. Traditionally, Opdyke [
16
] is referenced for publishing the
first PhD with respect to refactoring. However, in his PhD thesis, Opdyke refers to the PhD of
Griswold, acknowledging his precedence. Griswold did not use the word refactoring e
xplicitly,
yet referred to the same concept: automatable meaning preserving restructurings [
6
].


Clearly, refactoring is something all developers do, e
ven when
they

hand
-
code and do not realize
they
are

actually doing it. Despite its

association with Extre
me Programming (XP) and other radical
coding methods, refactoring

is a basic process.


When modifying
code to make it more rational, portable, modular, or readable,
much of the process
almost always consists
of applying refactoring methods
. These

method
s

systematically
restructure code
according to
implicit micro design rules. In the recent years

a number of
IDEs have been including
refactoring as part of the install
and/or providing as an add
-
on option to smoothly integrate
its
capabilities with the exi
sting development process
. These micro design rules force developers
to think
about their code structure and to identify
code
smell
s.


Code Smells


A code smell
,

also known as bad smell
,

is any symptom in the source code of a program that possibly
indic
ates a deeper problem.


Often the deeper problem hinted by a code smell can be uncovered when the code is subjected to a
short feedback cycle where it is refactored in small, controlled steps, and the resulting design is
examined to see if there are any fu
rther code smells that indicate the need of more refactoring. From
the point of view of a programmer charged with performing refactoring, code smells are heuristics to
indicate when to refactor, and what specific refactoring techniques to use. Thus, a code

smell is a driver
for refactoring.


The term appears to have been coined by Kent Beck on WardsWiki in the late 1990s. Usage of the
term increased after it was featured in Refactoring. Improv
ing the Design of Existing Code.
[9
]


Determining what is and is
not a code smell is often a subjective judgment, and will often vary by
language, developer and development methodology. There are tools, such as Checkstyle, PMD and
FindBugs for Java, to automatically check for certain kinds of code smells.


Since there i
s a large number of defined code smells, here is subset
of
the most commonly used
ones

divided into two categories
:


[9
]


Smells Within Classes




Duplicated Code:

The same code structure in two or more places is a good sign that the code
needs to be refact
ored: if you need to make a change in one place, you will probably need to
change the other one as well, but you might miss it



Long Method:

Long methods should be decomposed for clarity and ease of maintenance



Large Class:

Classes that trying to do too muc
h often have large numbers of instance variables.
Sometimes groups of variables can be clumped together. Sometimes they are only used
occasionally. Over
-
large classes can also suffer from code duplication.



Long Parameter List:

Long parameter lists are har
d to understand. You don’t need to pass in
everything a method needs so it can find all it needs.



Comments:
If the comments are present in the code because the code is bad, improve the
code.



Speculative Generality:
Often methods or classes are designed
to do things that in fact are not
required. The dead
-
wood should probably be removed.



Switch Statements:

Switch statements tend to cause duplication. You often find similar switch
statements scattered through the program in several places. If a new data

value is added to the
range, you have to check all the various switch statements. Maybe classes and polymorphism
would be more appropriate.



Smells Between Classes




Divergent Change:

Software should be structured for ease of change. If one class is ch
anged in
different ways for different reasons, it may be worth splitting the class in two so each one
relates to a particular kind of change



Shotgun Surgery:

If a type of problem change requires lots of little code changes in various
different classes, it
may be hard to find all the right places that do need changing. Maybe the
places that are affected should all be brought together into one class.



Feature Envy:

This is where a method on one class seems more interested in the attributes of
another class t
han in its own class. Maybe the method would be happier in the other class.



Data Clumps:

Sometimes you see the same bunch of data items together in various places:
fields in a couple of classes, parameters to methods, local data. Maybe they should be gro
uped
together into a little class.



Primitive Obsession:

Sometimes it’s worth turning a primitive data type into a lightweight class
to make it clear what it is for and what sort of operations are allowed on it (eg creating a date
class rather than using a

couple of integers)



Parallel Inheritance Hierarchies:
In this case, whenever you make a subclass of one class, you
have to make a subclass of another one to match.



Lazy Class:
Classes that are not doing much useful work should be eliminated



Temporary Fi
eld:
It can be confusing when some of the member variables in a class are only
used occasionally



Message Chains:
A client asks one object for another object, which is then asked for another
object, which is then asked for another, etc. This ties the cod
e to a particular class structure.



Middle Man:
Delegation is often useful, but sometimes it can go too far. If a class is acting as a
delegate, but is performing no useful extra work, it may be possible to remove it from the
hierarchy.



Inappropriate Inti
macy:
Two classes are overly intertwined.



Alternative classes with different interfaces:
Classes that do similar things, but have different
names, should be modified to share a common protocol.



Incomplete Library Class:
It’s bad form to modify the code
in a library, but sometimes they do
not do all they should do.



Data Class:
Classes that just have data fields, and access methods, but no real behavior.
Make
public data private.



Refused Bequest:
If a subclass does not want or need all of the behavior
of its base class,
maybe the class hierarchy is wrong.



After identification

of code smell
, the according code is changed based on a catalogue of change
steps referring to the problem. These steps range from renaming of variables, extraction of methods to

the extraction of complete classes from the existing code.


Design Guidelines

The concept of refactoring is an exploitation of Parnas’s design guidelines,

stating that structural
system modifications can be postponed by assuring locality of change for l
ikely changes [
21
]. To assure
locality, Parnas su
ggests the following steps:
identify the

items
that are likely to change,
locate
specialized components in separate
modules; and
design intermodule interfaces insensitive to the
anticipated changes [
21
]. The
se steps respectively correspond to three design principles:

abstraction,
encapsulation, and
information hiding.


These concepts are clearly distinguished by [
19
] based on an extensive literature survey:




Abstraction:

As a process, abstraction denotes ex
tracting the essential details about an item or a
group of items while ignoring the inessential details. Examples of these essential details are
difficult or likely
-
to
-
change design decisions. As an entity, abstraction denotes a model, a view,
or some oth
er focused representation for an actual item. Among others, abstraction differs from
information hiding in that it does not specify a mechanism for handling the unimportant
information.




Encapsulation:

As a process, encapsulation means the act of enclosin
g one or more items within
a (physical or logical) container. As an entity, encapsulation refers to a package or an enclosure
that holds (contains, encloses) one or more items. Encapsulation can be used as a strategy to
package

what has to be extracted, di
sregarding whether these items should be visible or hidden.




Information hiding:

The process of hiding information, especially difficult or likely
-
to
-
change
design decisions. Information hiding does not incorporate the identification of which
information

is to be hidden (e.g., through abstraction), nor the strategy for hiding them (e.g.,
through encapsulation).




Refactoring
Transformations

Fowler’s catalogue [9] lists
ninty
-
three
object
-
oriented refactorings and since then many others have
been discov
ered.

To demonstrate the possibility of reasoning about refactoring in terms of their impact
on program structure,
I will illustrate examples of
Extract

Method, Encaps
ulate Field,
Pull

Up

Method
,
Extract class, and Form Template Method

transformations
.

Th
ese refactorings

are quite typical for the
categories of refactoring strategies they belong to
-

respectively Composing Methods, Organizing

Data
and Dealing with Generalization
-

which are among the most popular refactoring s
trategies in today’s

refactorin
g tools

(
IntelliJ IDEA, Eclipse, Together
, Visual Studio etc..)
. Hence they may serve as
representatives for the complete set of primitive refactorings.




ExtractMethod
extracts part of a method and factors it out into a new method.

Before:


void printOwing
() {



printBanner();




//print details



System.out.println ("name:

" + _name);



System.out.println ("amount

" + getOutstanding());


}

After


void printOwing() {



printBanner();



printDetails(getOutstanding());


}



void printDetails (double outstandi
ng) {



System.out.println ("name:

" + _name);



System.out.println ("amount

" + outstanding);




EncapsulateField
encapsulates public attributes by making them private and providing
accessors. In other words, for each

public attribute a method is introduced

for accessing
(getting) and updating (setting) its value, and all direct references to the

attribute are replaced
by calls to these methods.

Before:


public String _name

After:

private String _name;

public String getName() {return _name;}

public void setN
ame(String arg) {_name = arg;}




PullUpMethod
moves identical methods from subclasses into a common superclass.





Extract Class
-

You have one class doing work that should be done by two.
Create a new class
and move the relevant fields and methods from the

old class into the new class.







Form Template Method

-

You have two methods in subclasses that perform similar steps in the
same order, yet the steps are different.

Get the steps into methods with the same signature, so
that the original methods become

the same. Then you can pull them up.




And here is a complete
list of available
refactoring methods where details of each can be found at
the following

link:
http://www.refactoring.com/catalog
/index.html




Add Parameter



Change Bidirectional Association to Unidirectional



Change Reference to Value



Change Unidirectional Association to Bidirectional



Change Value to Reference



Collapse Hierarchy



Consolidate Conditional Expression



Consolidate
Duplicate Conditional Fragments



Convert Dynamic to Static Construction by Gerard M. Davison



Convert Static to Dynamic Construction by Gerard M. Davison



Decompose Conditional



Duplicate Observed Data



Eliminate Inter
-
Entity Bean Communication (Link Only
)



Encapsulate Collection



Encapsulate Downcast



Encapsulate Field



Extract Class



Extract Interface



Extract Method



Extract Package by Gerard M. Davison



Extract Subclass



Extract Superclass



Form Template Method



Hide Delegate



Hide Method



Hide present
ation tier
-
specific details from the business tier (Link Only)



Inline Class



Inline Method



Inline Temp



Introduce A Controller (Link Only)



Introduce Assertion



Introduce Business Delegate (Link Only)



Introduce Explaining Variable



Introduce Foreign Met
hod



Introduce Local Extension



Introduce Null Object



Introduce Parameter Object



Introduce Synchronizer Token (Link Only)



Localize Disparate Logic (Link Only)



Merge Session Beans (Link Only)



Move Business Logic to Session (Link Only)



Move Class by
Gerard M. Davison



Move Field



Move Method



Parameterize Method



Preserve Whole Object



Pull Up Constructor Body



Pull Up Field



Pull Up Method



Push Down Field



Push Down Method



Reduce Scope of Variable by Mats Henricson



Refactor Architecture by Tiers

(Link Only)



Remove Assignments to Parameters



Remove Control Flag



Remove Double Negative by Ashley Frieze and Martin Fowler



Remove Middle Man



Remove Parameter



Remove Setting Method



Rename Method



Replace Array with Object



Replace Assignment with I
nitialization by Mats Henricson



Replace Conditional with Polymorphism



Replace Conditional with Visitor by Ivan Mitrovic



Replace Constructor with Factory Method



Replace Data Value with Object



Replace Delegation with Inheritance



Replace Error Code wit
h Exception



Replace Exception with Test



Replace Inheritance with Delegation



Replace Iteration with Recursion by Dave Whipp



Replace Magic Number with Symbolic Constant



Replace Method with Method Object



Replace Nested Conditional with Guard Clauses



Replace Parameter with Explicit Methods



Replace Parameter with Method



Replace Record with Data Class



Replace Recursion with Iteration by Ivan Mitrovic



Replace Static Variable with Parameter by Marian Vittek



Replace Subclass with Fields



Replace Temp w
ith Query



Replace Type Code with Class



Replace Type Code with State/Strategy



Replace Type Code with Subclasses



Reverse Conditional by Bill Murphy and Martin Fowler



Self Encapsulate Field



Separate Data Access Code (Link Only)



Separate Query from Mo
difier



Split Loop by Martin Fowler



Split Temporary Variable



Substitute Algorithm



Use a Connection Pool (Link Only)



Wrap entities with session (Link Only)




Metrics

As the list of object
-
oriented program quality metrics is virtually endless (i.e.
[24]

alone describes
more than 200 complexity metrics),
I
will focus on those program metrics which are most commonly
used, being Number of Methods, Cyclomatic Complexity, Number of Children, Coupling Between
Objects, Response For a Class and Lack of Cohesion
among Methods.



Definitions for these metrics are:



Number of Methods
calculates the number of methods of a class. It is an indicator of the
functional size of a class.



Cyclomatic Complexity
counts the number of possible paths through an algorithm. It is
an
indicator of the logical complexity

of a program, based on the number of flow graph edges and
nodes [7].



Number of Children
measures the immediate descendants of a class [5]. It is an indicator of the
generality of the class.



Coupling Between Objects
is

a measure for the number of collaborations for a class [18]. It is an
indicator of the complexity

of the conceptual functionality implemented in the class.



Response For a Class
is the number of both defined and inherited methods of a class, including
meth
ods of other classes

called by these methods [5]. It is an indicator of the vulnerability to
change propagations of the class.



Lack of Cohesion among Methods
is an inverse cohesion measure (high value means low
cohesion). Of the many variants

of LCOM, we u
se LCOM1 as defined by Henderson
-
Sellers [10]
as the number of pairs of methods in a class having no

common attribute references. It is an
indicator of how well the methods of the class fit together.


Ho
w to achieve well formed object
-
oriented method

In or
der to achieve
a well formed object
-
oriented method
the following requirements must be met:



acceptably cohesive [10, 19],



low in complexity [12],



appropriately sized,



independently testable [3], and



well documented.


Acceptably Cohesive

One of the most imp
ortant characteristics of a well formed object oriented method is cohesion.
Method cohesion is defined as a measure of how well the elements within a module work together
to provide a specific functionality. While cohesion was first used in structured desi
gn [22, 5, 14],
method cohesion has been adapted for object
-
oriented software. Kang [10] defines six levels of
object
-
oriented method cohesion ranked from best to worst as shown in Figure 1. The highest level,
functional cohesion, deals

with the ability of

a module to produce one output for one module (i.e.,
to change the state of one object). As shown by Kang’s empirical evidence, strongly cohesive
methods are desired because the stronger the cohesion, the easier the method is to maintain,
understand, and
reuse [10].



Functional

Only one output exist for the
module

Sequential

One output is dependent on the other output

Communicational

Two outputs are dependent on a common input

Iterative

Two outputs are iteratively dependent on the
same input

Condit
ional

Two outputs are conditionally dependent on the
same input

Coincidental

Two outputs have no dependence relationship
which each other and no dependence relation on a
common input


Figure 1

Cohesion in Object
-
oriented Design




Cyclomatic

Complexity

H
aving a low level of complexity is another important characteristic of a well formed object
-
oriented
method. Students are introduced to McCabe’s Cyclomatic Complexity [12] as the metric for
measuring complexity of their code. McCabe’s Cyclomatic Complexity
, which measures the number
of linearly

independent paths within code, is defined as the number of decision points + 1 where
decision points are conditional statements such as
if/else
or
while
. Figure 2

shows McCabe’s
complexity scale. The goal is code tha
t is not very complex and, therefore, low risk. Low level of
complexity within a method

makes methods more understandable and maintainable [18].



Number of Paths

Code Complexity

Risk

1
-
10

Not very complex

Low

11
-
20

Moderately complex

Moderate

21
-
50

Hig
hly complex

High

51+

Unstable

Very high


Figure 2

McCabe’s Complexity Scale




Appropriately Sized

The third characteristic of a well formed object
-
oriented method is size. One of the techniques for
determining the size of software is counting its lines
of code (LOC). The method of determining

LOC depends on how the executable lines, blank lines, comment lines, data declarations, and
multiple line statements are treated. LOC is considered inappropriate for measuring the quality of
object
-
oriented classes

[2, 15]. However, it is useful in measuring object
-
oriented methods since
studies have shown that large methods result in a reduction of understandability, reusability,
testability, and maintainability [9, 11, 21, 17, 18, 6]. The appropriate size of a met
hod depends on
the programming

language being used and the application being developed. Multiple line
statements such as
if/else
and
case
should be

counted as one statement with each executable line
within these multiple line statements also being counted
. A strict rule for method size is enforced to
encourage
developers
to produce small, functionally cohesive methods.
Developers should

limit the
size of a method to 10 lines of C++ or Java

or any other

code with a possibility of up to 20 lines of
code if j
ustified by two lines of comments per extra line.



Independently Testable

The testing of software is critical in software development and, therefore, an essential concept to
teach.
Each developer should have the knowledge and understanding of functional t
esting and
unit
testing.
This familiarity will help to
build independently testable methods which improves the
effectiveness of unit testing.


A method is considered independently testable [3] if it meets the following criteria:



The method is invokable b
y a method call with parameters to set particular values.



The method output can be inspected by validating the return values.



The method output is unique to that method.


Well Documented

Making software well documented is
very important
.
The best practic
e is to name your classes and
method names appropriately so that the code is self descriptive.
The effectiveness of well

named program elements and meaningful comments is widely recognized [8, 4, 16]. Comments are
typically measured by comment percentage

with approximately 30 percent being most effective
[18]. It is calculated by dividing the total number of comments by the total lines of code less the
number of blank lines. Well documented software is known to improve the understandability,
reusability,
and maintainability of code. Research also shows that method documentation improved
understandability more than class documentation [13].







Automated
C
ode
R
efactoring

Tools

Many software editors and IDEs have automated refactoring support. Here is a

list of a few of these
editors, or so
-
called refactoring browsers.



IntelliJ IDEA (for Java)



Eclipse's Java Development Toolkit (JDT)



NetBeans (for Java)



Embarcadero Delphi



Visual Studio (for .NET)



ReSharper (An addon for Visual Studio)



Visua
l Assist (An addon for Visual Studio with refactoring support for VB, VB.NET. C# and C++)



DMS Software Reengineering Toolkit (Implements large
-
scale refactoring for C, C++, C#, COBOL,
Java, PHP and other languages)



Photran a Fortran plugin for the Ecli
pse IDE



SharpSort addin for Visual Studio 2008



Sigasi HDT (for VHDL)



Conclusion

Refactoring is
proven

to positively affect non
-
functional aspects,
such as
extensibility, modularity,
reusability, complexity, maintainability, and efficiency as stated i
n
[23]
. However, additional negative
aspects of refactoring are reported as well. They consist of additional memory consumption, higher
power consumption, longer execution time, and lower suitability for safety critical applications.
Therefore, before re
factoring is applied to the software system the developer should analyze which
quality characteristics are the most important
to achieve for a particular, module, etc..
and only then
he/she should
take necessary steps to
continue with the rest of the proce
ss.



References

[1] Beck, K.
Extreme Programming Explained: Embrace Change.
Addison
-
Wesley, Reading, MA, 2000.

[2]
T. Mens, .A survey of software refactoring,.
IEEE Transactions on Software Engineering
, vol. 30,

no. 2, pp. 126.139, February 2004.

[3] V.
R. Basili andW. L. Melo. A validation of object
-
oriented design metrics as quality indicators.

IEEE Trans. Software Engineering
, 22(10):751

761, October 1996.

[4] Deursen, A. and Moonen, L. (2002). The video store revisited
-

thoughts on refactoring and
t
esting. In Marchesi, M. and Succi, G., editors,
Proceedings of the 3nd International Conference on
Extreme Programming and Flexible Processes in Software Engineering (XP2002)
, pages 71

76.
University of Cagliari.

[5] S. R. Chidamber and C. F. Kemerer. A me
trics suite for object
-
oriented design.
IEEE Trans.

Software Engineering
, 20(6):476

493, June 1994.

[6] Griswold, W. (1991).
Program Restructuring as an Aid to Software Maintenance
. PhD

thesis, University of Washington.

[7] J. C. Coppick and T. J. Cheath
am. Software metrics for object
-
oriented systems. In
Proceedings of

the 1992 ACM annual conference on Communications
, pages 317

322. ACM Press, 1992.

[8] Wake, W. C. (2003).
Refactoring Workbook
. Addison
-
Wesley Longman Publishing Co., Inc., Boston,

MA, U
SA.

[9] M. Fowler.
Refactoring: Improving the Design of Existing Programs
. Addison
-
Wesley, 1999.

[10] Kang, B., and Bieman, J.M. A quantitative framework for software restructuring.
Journal of

Software Maintenance: Research and Practice, 11
, 4 (July/Augus
t 1999), 245
-
284.

[10]
[Khan et al., 1996] Khan, M. K., Rashid, M. A., and Lo, B. W. (1996). A task
-
oriented software

maintenance model.
Malaysian Journal of Computer

[11] Kataoka, Y., Imai, T., Andou, H., and Fukaya, T. (2002). A quantitative evaluation
of

maintainability enhancement by refactoring. In
Proc. Int’l Conf. Software Maintenance
, pages
576

585. IEEE Computer Society Press.

[12] McCabe. T. A software complexity measure.
IEEE Trans. On Softw. Eng.
,
2,
4 (December 1976),

308
-
320.

[13
] DeMarco,
T.
Structured Analysis and System Specification
. Yourdon Press, New York, NY, 1978.

[14]Page
-
Jones, M.
The Practical Guide to Structured Systems Design
. Yourdon Press, New York, NY,

1980.

[15
] Simon, F., Steinbr
uckner, F., and Lewerentz, C. (2001). Metric
s based refactoring. In

Proc. European Conf. Software Maintenance and Reengineering
, pages 30

38. IEEE Computer

Society Press.


[16] W. Opdyke.
Refactoring Object
-
Oriented Frameworks
. PhD thesis, University of Illinois at

Urbana
-
Champaign, 1992.

[17] W.

Opdyke and R. Johnson. Creating abstract superclasses by refactoring. In
Proc. ACM

Computer Science Conference
, pages 66

73. ACM Press, 1993.

[18] Rosenberg, L. and Hyatt, L.
Software quality metrics for object
-
oriented system environments
.

SATC, NASA
Technical Report SATC
-
TR
-
95
-
1001, 1995.

[19] Berard, E. V. (1993).
Essays on Object
-
Oriented Software Engineering
. Prentice
-
Hall, Inc.,

Englewood Cliffs, New Jersey.

[21] Parnas, D. L. (1978). Designing software for ease of extension and contraction. In
I
CSE ’78:

Proceedings of the 3rd international conference on Software engineering
, pages 264

277,
Piscataway, NJ, USA. IEEE Press.

[22]Yourdon, E. and Constantine, L.
Structured Design
. Yourdon Press, New York, NY, 1978.

[23]

T. Mens and T. Tourw´e. A surv
ey of software refactoring.
IEEE Transactions on Software

Engineering
, 30(2), 2004.

[24]

H. Zuse.
Software Complexity
. Walter de Gruyter, Berlin, 1991.

[25]
Science
, 9(2):36

42.
B. Henderson
-
Sellers.
Object
-
Oriented Metrics: Measures of Complexity
.

Prentic
e
-
Hall, 1996.

http://www.refactoring.com