Presentation is loading. Please wait.

Presentation is loading. Please wait.

OORPT Object-Oriented Reengineering Patterns and Techniques 8. Restructuring Prof. O. Nierstrasz.

Similar presentations


Presentation on theme: "OORPT Object-Oriented Reengineering Patterns and Techniques 8. Restructuring Prof. O. Nierstrasz."— Presentation transcript:

1 OORPT Object-Oriented Reengineering Patterns and Techniques 8. Restructuring Prof. O. Nierstrasz

2 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.2 Roadmap  Redistribute Responsibilities  Transform Conditionals to Polymorphism

3 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.3 Tests: Your Life Insurance Detailed Model Capture Initial Understanding First Contact Setting Direction Migration Strategies Detecting Duplicated Code Redistribute Responsibilities Transform Conditionals to Polymorphism A Map of Reengineering Patterns

4 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.4 The Reengineering Life-Cycle Requirements Designs Code (0) requirement analysis (1) model capture (2) problem detection (3) problem resolution (4) program transformation (2) problem detection (3) problem resolution issues OO paradigm When/When not

5 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.5 Roadmap  Redistribute Responsibilities —The Law of Demeter —Move Behaviour Close to Data —Eliminate Navigation Code —Split Up God Class  Transform Conditionals to Polymorphism

6 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.6 Redistribute Responsibilities Eliminate Navigation Code Data containers Monster client of data containers Split Up God Class Move Behaviour Close to Data Chains of data containers

7 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.7 Roadmap  Redistribute Responsibilities —The Law of Demeter —Move Behaviour Close to Data —Eliminate Navigation Code —Split Up God Class  Transform Conditionals to Polymorphism

8 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.8 The Law of Demeter  A method M of an object O should invoke only the methods of the following kinds of objects: 1.O itself 2.parameters of M 3.any object M creates /instantiates 4.direct component objects of O http://en.wikipedia.org/wiki/Law_of_Demeter

9 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.9 Indirect Provider doSomething() Immediate Provider +provider getProvider() Indirect Client intermediate.provider.doSomething() Or intermediate.getProvider().doSomething() intermediate.provider.doSomething() Or intermediate.getProvider().doSomething() provider intermediate The Core of the Problem

10 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.10 Roadmap  Redistribute Responsibilities —The Law of Demeter —Move Behaviour Close to Data —Eliminate Navigation Code —Split Up God Class  Transform Conditionals to Polymorphism

11 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.11 Move Behavior Close to Data  Problem: How do you transform a data container into a service provider  Solution: Move behavior defined by indirect clients to the class defining the data they manipulate  …however —Visitor —Difficult to identify client code to be moved in – Responsibility of the provider – Access attributes of the provider – Accessed by multiple clients

12 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.12 Transformation… Provider +x +y +sety(val) +setx(val) Client Op2() … provider.sety(provider.x + provider.y) … provider.sety(provider.x + provider.y) … Provider -x -y -sety(val) +bump() Client Op2() … provider.bump() … provider.bump() … this.sety(provider.x + provider.y)

13 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.13 Detection strategy  Look for data containers —Classes with only accessors  Duplicated client code  Methods using sequence of accessors

14 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.14 Difficulties  When the moved behavior accessed client data, having extra parameters can lead to complex interface  Certain classes (Set or Stream) are data containers. Move functionality to provider if —It represents a provider responsibility —It accesses attributes of the provider —The same behavior defined in multiple clients

15 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.15 When Legacy Solution is not a Problem  A Visitor typically defines behavior that acts on another class  Configuration classes (global settings, language dependent information..)  Mapping classes between objects and UI or databases representation

16 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.16 Roadmap  Redistribute Responsibilities —The Law of Demeter —Move Behaviour Close to Data —Eliminate Navigation Code —Split Up God Class  Transform Conditionals to Polymorphism

17 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.17 Eliminate Navigation Code  Problem: How do you reduce the coupling due to classes that navigate object graph?  Solution: iteratively move behavior close the data —…however – Systematic uses produce large interfaces (shield collections) – AKA Law of Demeter

18 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.18 Eliminate Navigation Code … engine.carburetor.fuelValveOpen = true … engine.carburetor.fuelValveOpen = true Engine +carburetor Car -engine +increaseSpeed() Carburetor +fuelValveOpen Engine -carburetor +speedUp() Car -engine +increaseSpeed() … engine.speedUp() … engine.speedUp() carburetor.fuelValveOpen = true Carburetor +fuelValveOpen Car -engine +increaseSpeed() Carburetor -fuelValveOpen +openFuelValve() Engine -carburetor +speedUp() carburetor.openFuelValve() fuelValveOpen = true

19 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.19 Detection (i)  Class with lot of accessors few methods  Each time a class changes, indirect clients get impacted  Search for patterns —a.b.c.d.op() identified by – egrep ‘.*\..*\..*\..’ *.java —anObject.m1().m2().op() identified by – egrep ‘.*\(\).*\(\).*\(\).’ *.java

20 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.20 Detection (ii)  Not a problem —(a.isNode()) & (a.isAbstract())  Beware of disguised navigation Token token; token = parseTree.token(); if (token.identifier() != null){…  if(parseTree.token().identifier() != null){…

21 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.21 When the Legacy Solution is the Solution  User Interfaces or databases may need to have access to indirect providers  Brokers or object servers are special objects returning objects

22 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.22 Law of Demeter’s Dark Side  Can produce large interfaces Class A instVar: myCollection A>>do: aBlock myCollection do: aBlock A>>collect: aBlock myCollection collect: aBlock …

23 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.23 Roadmap  Redistribute Responsibilities —The Law of Demeter —Move Behaviour Close to Data —Eliminate Navigation Code —Split Up God Class  Transform Conditionals to Polymorphism

24 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.24 Split Up God Class  Problem: How to break a class that controls the complete system logic?  Solution: Incrementally distribute responsibilities into slave classes —…however it is difficult to – Identify abstractions in blob – Limit impact of changes on other parts

25 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.25 Detection  Huge, monolithic class with no clear and simple responsibility  “The heart of the system”  One single class contains all the logic and control flow  Other classes only serve as passive data holders  Names like “Manager”, “System”, “Root”, “*Controller*”  Changes to the system always entail changes to the same class

26 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.26 Transformation  Difficult because God Class is a usually a huge blob  Identify cohesive set of attributes and methods —Create classes for these sets  Identify all classes used as data holder and analyze how the god class uses them —Itaeratively Move Behavior close to the Data  Try to always have a running system before decomposing the God Class —Use accessors to hide the transformation —Use method delegation from the God Class to the providers —Use Façade to minimize change in clients

27 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.27 Strategies  If God Class does not need to be changed don’t touch it!  Wrap it with different OO views —but a God Class usually defines the control flow of the application

28 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.28 Roadmap  Redistribute Responsibilities  Transform Conditionals to Polymorphism —Transform Self Type Checks —Transform Client Checks —[Factor out State] —Factor out Strategy —Introduce Null Object —Transform Conditionals into Registration

29 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.29 A Map of Reengineering Patterns Tests: Your Life Insurance Detailed Model Capture Initial Understanding First Contact Setting Direction Migration Strategies Detecting Duplicated Code Redistribute Responsibilities Transform Conditionals to Polymorphism

30 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.30 Transform Self Type Checks Test provider type Test self type Test external attribute Transform Client Type Checks Transform Conditionals into Registration Test null values Introduce Null Object Factor Out Strategy Factor Out State Test object state Transform Conditionals to Polymorphism

31 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.31 Forces  Requirements change, so new classes and new methods will have to be introduced  Adding new classes may clutter the namespace  Conditionals group all the variant in one place but make changes difficult —Conditionals clutter logic —Editing several classes and fixing case statements to introduce a new behavior is error prone

32 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.32 Overview  Transform Self Type Checks —eliminates conditionals over type information in a provider by introducing new subclasses  Transform Client Checks —eliminates conditionals over client type information by introducing new method to each provider classes  Factor out State —kind of Self Type Check  Factor out Strategy —kind of Self Type Check  Introduce Null Object —eliminates null tests by introducing a Null Object  Transform Conditionals into Registration —eliminates conditional by using a registration mechanism

33 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.33 Roadmap  Redistribute Responsibilities  Transform Conditionals to Polymorphism —Transform Self Type Checks —Transform Client Checks —[Factor out State] —Factor out Strategy —Introduce Null Object —Transform Conditionals into Registration

34 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.34 A m() Client … case Text: this.doSomething() case Border: this.doOther() case D: … case Text: this.doSomething() case Border: this.doOther() case D: Transform Self Type Checks  Symptoms —Simple extensions require many changes in conditional code —Subclassing impossible without duplicating and updating conditional code —Adding new case to conditional code

35 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.35 A m() Client … case Text: this.doSomething() case Border: case D: … case Text: this.doSomething() case Border: case D: Client A m() hook() this.doSomething() … this.hook() … this.hook() Text hook() Border hook() D hook() Transformation

36 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.36 Example: Transform Self Type Checks class Message { private: int type_; void* data;... void send (Channel* ch) { switch (type_) { case TEXT : { ch->nextPutAll(data); break; } case ACTION : { ch->doAction(data);...

37 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.37 Message send() Message send() ActionMessage send() TextMessage send() switch (type_) { case TEXT : { ch->nextPutAll(data); break;} case ACTION : { ch->doAction(data);... switch (type_) { case TEXT : { ch->nextPutAll(data); break;} case ACTION : { ch->doAction(data);... Client1 Client2 Transform Self Type Check

38 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.38 Detection  Long methods with complex decision logic —Look for attribute set in constructors but never changed —Attributes to model type or finite set constants —Multiple methods switch on the same attribute —grep switch ‘find. -name “*.cxx” -print’

39 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.39 Pros/Cons/Difficulties  Pros —New behavior are easy to add and to understand: a new class —No need to change different method to add a behavior —All behaviors share a common interface  Cons —Behavior are dispersed into multiple but related abstractions —More classes  Difficulties —Not always one to one mapping between cases and subclasses —Clients may be changed to create instance of the right subclass

40 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.40 Roadmap  Redistribute Responsibilities  Transform Conditionals to Polymorphism —Transform Self Type Checks —Transform Client Checks —[Factor out State] —Factor out Strategy —Introduce Null Object —Transform Conditionals into Registration

41 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.41 A init() Client a : A m() switch (a.class) case B: a.init(); ((B) a).x(); case C: a.init(); ((C)) a).y(); Case D: ((D) a).z() switch (a.class) case B: a.init(); ((B) a).x(); case C: a.init(); ((C)) a).y(); Case D: ((D) a).z() B x() C init() Y() D z() Transform Client Type Checks  Symptoms —Clients perform explicit type checks / type coercions —Adding a new provider (A subclass)  change all clients —Clients are defining logic about providers

42 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.42 Transformation A init() Client a : A m() switch (a.class) case B: a.init(); ((B) a).x(); case C: a.init(); ((C)) a).y(); Case D: ((D) a).z() Client a : A m() a.doit(); A init() doit() B x() doit() C init() Y() doit() D z() doit() this.init (); this.x(); this.init (); this.x(); this.init (); this.y(); this.init (); this.y(); this.z(); B x() C init() Y() D z()

43 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.43 Example: Transform Client Type Checks void makeCalls (Telephone* phoneArray[]) { for (Telephone *p = phoneArray; p; p++) { switch (p-> phoneType()) { case TELEPHONE::POTS : { POTSPhone* potsp = (POTSPhone*)p potsp->tourne(); potsp->call();... case TELEPHONE::ISDN : { ISDNPhone* isdnp = (ISDNPhone*)p isdnp->initLine(); isdnp->connect();...

44 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.44 TelephoneBox makeCall () Telephone POTSPhone... ISDNPhone... TelephoneBox makeCall () Telephone makeCall() POTSPhone makeCall()... ISDNPhone makeCall... Transform Client Type Check

45 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.45 Detection  Transform Self Type Checks  Changing clients of method when new case added  Attribute representing a type In Smalltalk: isKindOf:, isMemberOf:  In Java: instanceof  x.getClass() == y.getClass()  x.getClass().getName().equals(….)

46 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.46 Pros/Cons/Difficulties  Pros —The provider offers now a polymorphic interface that can be used by other clients —A class represent one case —Clients are not responsible of provider logic —Adding new case does not impact all clients  Cons —Behavior is not group per method but per class  Difficulties —Refactor the clients (Deprecate Obsolete Interfaces) —Instance creation should not be a problem

47 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.47 When the Legacy Solution is the Solution  Abstract Factory may need to check a type variable to know which class to instantiate. —For example streaming objects from a text file requires to know the type of the streamed object to recreate it  If provider hierarchy is frozen —Wrapping the classes could be a good migration strategy  Software that interfaces with non-OO libraries —Switch to simulate polymorphic calls

48 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.48 Roadmap  Redistribute Responsibilities  Transform Conditionals to Polymorphism —Transform Self Type Checks —Transform Client Checks —[Factor out State] —Factor out Strategy —Introduce Null Object —Transform Conditionals into Registration

49 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.49 Factor Out Strategy  Problem: —How do you make a class whose behavior depends on testing certain values more extensible?  Solution: —Apply Strategy Pattern —Encapsulate the behavior and delegate using a polymorphic call

50 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.50 Transformation AbstractStrategy handleOperation() A operation() … strategy.handleOperation() … strategy.handleOperation() … StrategyX handleOperation() A operation() … case X: … case Z: …. … case X: … case Z: …. … strategy StrategyZ handleOperation()

51 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.51 Pros/Cons/Difficulties  Pros —Behavior extension is well-identified —Behavior using the extension is clearer —Change behavior at run-time  Cons —Namespace get cluttered —Yet another indirection  Difficulties —Behavior can be difficult to convert and encapsulate (passing parameter…)

52 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.52 Roadmap  Redistribute Responsibilities  Transform Conditionals to Polymorphism —Transform Self Type Checks —Transform Client Checks —[Factor out State] —Factor out Strategy —Introduce Null Object —Transform Conditionals into Registration

53 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.53 Introduce NullObject  Problem: —How can you avoid repeated tests for null values?  Solution: —Encapsulate the null behavior as a separate class that is polymorphic to the provider

54 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.54 Transformation AbstractObject doit() Client m() RealObject doit() nothing NullObject doit() RealObject doit() Client m() … a.doit() … a.doit() … if (a!=Null) { a.doit()} … if (a!=Null) { a.doit()} …

55 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.55 Pros/Cons/Discussions  Pros —Clients do not need to test for null values  Difficulties —Different clients may have different null behavior —In statically typed languages, you have to introduce Null interface  Hint —The NullObject does not have to be a subclass of RealObject superclass as soon as it implements RealObject’s null interface (in Java and Smalltalk)  Do not apply when —Very little code uses direct variable access —Code that checks is well-encapsulated in a single place

56 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.56 Roadmap  Redistribute Responsibilities  Transform Conditionals to Polymorphism —Transform Self Type Checks —Transform Client Checks —[Factor out State] —Factor out Strategy —Introduce Null Object —Transform Conditionals into Registration

57 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.57 Transform Conditional into Registration  Problem —How do you reduce the coupling between tools providing services and clients so that addition/removal of tools does not change client code?  Solution: Introduce a registration mechanism —Tools register/unregister —Clients query them via the registration repository

58 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.58 Symptoms  Long methods in clients checking which tools to invoke based on external properties e.g., file extension  Removing or adding a tool forces you to change client code  Difficulty to have run-time tool loading / unloading

59 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.59 Transformation (i) ToolClient read() XMLReader openFile (File) WordReader on (file) suffix := selectedFile suffix = ‘xml’. suffix = ‘xml’ ifTrue: [ XMLReader openFile: selectedFile. ^ self] suffix = ‘doc’ ifTrue: [WordReader on: selectedFile. ^ self]. … suffix := selectedFile suffix = ‘xml’. suffix = ‘xml’ ifTrue: [ XMLReader openFile: selectedFile. ^ self] suffix = ‘doc’ ifTrue: [WordReader on: selectedFile. ^ self]. …

60 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.60 Transformation (ii) XMLReader openFile (File) load() unload() ToolClient read() (PluginManager uniqueInstance findToolFor: selectedFile suffix) action (PluginManager uniqueInstance findToolFor: selectedFile suffix) action WordReader on (file) load() unload() Plugin action for: String use: class with: method PluginManager add/remove (Tool) findToolFor (String) (PluginManager uniqueInstance add: (Plugin for: ‘xml’ use: XMLReader with: openFile) (PluginManager uniqueInstance add: (Plugin for: ‘xml’ use: XMLReader with: openFile) (PluginManager uniqueInstance remove: (Plugin for: ‘xml’ use: XMLReader with: openFile) (PluginManager uniqueInstance remove: (Plugin for: ‘xml’ use: XMLReader with: openFile)

61 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.61 Pros/Cons/Difficulties  Pros —New tools can be added without impacting clients —Interaction between tools and clients is normalized —Reduce coupling and support modular design  Cons —Every tool should register and unregister  Difficulties —Action should be defined on the tool and not the client anymore, information should be passed from the client to the tool —Client knew statically the tools, now it is dynamic so more effort for UI (i.e., consistent menu ordering)

62 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.62 Conclusion  Navigation Code & Complex Conditionals —Most common lack of OO use  Polymorphism is key abstraction mechanism —adds flexibility  reduces maintenance cost  Avoid Risk —Only refactor when inferior code must be changed (cf. God Class)  Performance? —Small methods with less navigation code are easier to optimise —Deeply nested if-statements cost more than virtual calls Long case statements cost as much as virtual calls

63 © Stéphane Ducasse, Serge Demeyer, Oscar Nierstrasz OORPT — Restructuring 8.63 License > http://creativecommons.org/licenses/by-sa/2.5/ Attribution-ShareAlike 2.5 You are free: to copy, distribute, display, and perform the work to make derivative works to make commercial use of the work Under the following conditions: Attribution. You must attribute the work in the manner specified by the author or licensor. Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one. For any reuse or distribution, you must make clear to others the license terms of this work. Any of these conditions can be waived if you get permission from the copyright holder. Your fair use and other rights are in no way affected by the above. Attribution-ShareAlike 2.5 You are free: to copy, distribute, display, and perform the work to make derivative works to make commercial use of the work Under the following conditions: Attribution. You must attribute the work in the manner specified by the author or licensor. Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one. For any reuse or distribution, you must make clear to others the license terms of this work. Any of these conditions can be waived if you get permission from the copyright holder. Your fair use and other rights are in no way affected by the above.


Download ppt "OORPT Object-Oriented Reengineering Patterns and Techniques 8. Restructuring Prof. O. Nierstrasz."

Similar presentations


Ads by Google