ObjectOriented Reengineering Patterns A Map of Reengineering Patterns

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

Object-Oriented Reengineering Patterns Setting Direction © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 2

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

Object-Oriented Reengineering Patterns The Reengineering Life-Cycle (0) requirement analysis Requirements (2) problem detection Designs (1) model capture (3) problem resolution (0) req. analysis (1) model capture issues • scale • speed • accuracy • politics Code (4) program transformation © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 4

Object-Oriented Reengineering Patterns Forces — Setting Direction > Conflicting interests (technical, ergonomic, economic, political) > Presence/absence original developers > Legacy architecture > Which problems to tackle? — Interesting vs important problems? — Wrap, refactor or rewrite? © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 5

Object-Oriented Reengineering Patterns Setting Direction Set direction Where to start Agree on Maxims Maintain direction Appoint a Navigator Coordinate direction Speak to the Round Table Most Valuable First What to do Fix Problems, Not Symptoms Principles & Guidelines for Software project management especially relevant for reengineering projects © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz What not to do If It Ain't Broke Don't Fix It How to do it Keep it Simple 6

Object-Oriented Reengineering Patterns Most Valuable First > Problem: Which problems should you focus on first? > Solution: Work on aspects that are most valuable to your customer — Maximize commitment, early results; build confidence — Difficulties and hints: – – – Which stakeholder do you listen to? What measurable goal to aim for? Consult change logs for high activity Play the Planning Game Wrap, refactor or rewrite? — Fix Problems, not Symptoms © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 7

Object-Oriented Reengineering Patterns First Contact © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 8

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

Object-Oriented Reengineering Patterns Forces — First Contact > Legacy systems are large and complex — Split the system into manageable pieces > Time is scarce — Apply lightweight techniques to assess feasibility and risks > First impressions are dangerous — Always double-check your sources > People have different agendas — Build confidence; be wary of skeptics © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 10

Object-Oriented Reengineering Patterns First Contact System experts Talk with end users Talk with developers Chat with the Maintainers Verify what you hear Feasibility assessment (one week time) Talk about it Interview during Demo Software System Read it Read about it Compile it Read All the Code Skim the Do a Mock in One Hour Documentation Installation © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 11

Object-Oriented Reengineering Patterns Interview during Demo > Problem: What are the typical usage scenarios? > Solution: Ask the user! • Solution: interview during demo - select several users - demo puts a user in a positive —. . . however mindset – Which user ? - demo steers the interview – – Users complain What should you ask ? © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 12

Object-Oriented Reengineering Patterns Chat with the Maintainers > Problem: What are the history and politics of the legacy system? > Solution: Discuss the problems with the system maintainers. — Documentation will mislead you (various reasons) — Stakeholders will mislead you (various reasons) — The maintainers know both the technical and political history © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 13

Object-Oriented Reengineering Patterns Chat with the Maintainers > Questions to ask: — Easiest/hardest bug to fix in recent months? — How are change requests made and evaluated? — How did the development/maintenance team evolve during the project? — How good is the code? The documentation? — Why was the reengineering project started? What do you hope to gain? — The major problems of our work are no so much technological as sociological. – De. Marco and Lister, Peopleware ‘ 99 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 14

Object-Oriented Reengineering Patterns Read all the Code in One Hour I took a course in speed reading and read “War and Peace” in twenty minutes. It’s about Russia. —Woody Allen > Problem: How can you get a first impression of the quality of the source code? > Solution: Scan all the code in single, short session. — Use a checklist (code review guidelines, coding styles etc. ) — Look for functional tests and unit tests — Look for abstract classes and root classes that define domain abstractions — Beware of comments — Log all your questions! © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 15

Object-Oriented Reengineering Patterns First Project Plan > Use standard templates, including: — project scope – see "Setting Direction" — opportunities – e. g. , skilled maintainers, readable source-code, documentation — risks – – e. g. , absent test-suites, missing libraries, … record likelihood (unlikely, possible, likely) & impact (high, moderate, low) for causing problems — go/no-go decision — activities – fish-eye view © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 16

Object-Oriented Reengineering Patterns Initial Understanding © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 17

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

Object-Oriented Reengineering Patterns Forces — Initial Understanding > Data is deceptive — Always double-check your sources > Understanding entails iteration — Plan iteration and feedback loops > Knowledge must be shared — “Put the map on the wall” > Teams need to communicate — “Use their language” © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 19

Object-Oriented Reengineering Patterns Initial Understanding Top down Recover design Speculate about Design understand higher-level model Analyze the Persistent Data Study the Exceptional Entities Compile it Read it Bottom up © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 20

Object-Oriented Reengineering Patterns Speculate about Design > Problem: How do you recover design from code? > Solution: Develop hypotheses and check them — Develop a plausible class diagram and iteratively check and refine your design against the actual code. — Variants: – – – Speculate about Business Objects Speculate about Design Patterns Speculate about Architecture © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 21

Object-Oriented Reengineering Patterns Study the Exceptional Entities > Problem: How can you quickly identify design problems? > Solution: Measure software entities and study the anomalous ones — Use simple metrics — Visualize metrics to get an overview — Browse the code to get insight into the anomalies © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 22

Object-Oriented Reengineering Patterns Visualizing Metrics Use simple metrics and layout algorithms height (x, y) colour width Visualizes up to 5 metrics per node © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 23

Object-Oriented Reengineering Patterns Initial Understanding (revisited) Top down Recover design Speculate about Design understand higher-level model ITERATION Analyze the Persistent Data Study the Exceptional Entities Recover database Identify problems Bottom up © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 24

Object-Oriented Reengineering Patterns Detailed Model Capture © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 25

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

Object-Oriented Reengineering Patterns Forces — Detailed Model Capture > Details matter — Pay attention to the details! > Design remains implicit — Record design rationale when you discover it! > Design evolves — Important issues are reflected in changes to the code! > Code only exposes static structure — Study dynamic behaviour to extract detailed design © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 27

Object-Oriented Reengineering Patterns Detailed Model Capture Tie Code and Questions Keep track of your understanding Expose design Expose the design & make sure it remains exposed Refactor to Understand Expose collaborations Step through the Execution Expose contracts Write Tests to Understand Look for the Contracts • • • Use Your Tools Look for Key Methods Look for Constructor Calls Look for Template/Hook Methods Look for Super Calls © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz Expose evolution Learn from the Past 28

Object-Oriented Reengineering Patterns Tie Code and Questions > Problem: How do you keep track of your understanding? > Solution: Annotate the code — — List questions, hypotheses, tasks and observations Identify yourself! Use conventions to locate/extract annotations Annotate as comments, or as methods © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 29

Object-Oriented Reengineering Patterns Refactor to Understand > Problem: How do you decipher cryptic code? > Solution: Refactor it until it makes sense — Goal (for now) is to understand, not to reengineer — Work with a copy of the code — Refactoring requires an adequate test base – If this is missing, Write Tests to Understand — Hints: – – Rename attributes to convey roles Rename methods and classes to reveal intent Remove duplicated code Replace condition branches by methods © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 30

Object-Oriented Reengineering Patterns Step Through the Execution > Problem: How do you uncover the run-time architecture? > Solution: Execute scenarios of known use cases and step through the code with a debugger — Tests can also be used as scenario generators – If tests are missing Write Tests to Understand — Put breakpoints in the code — Difficulties – – – OO source code exposes a class hierarchy, not the run-time object collaborations Collaborations are spread throughout the code Polymorphism may hide which classes are instantiated — Focused use of a debugger can expose collaborations © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 31

Object-Oriented Reengineering Patterns Look for the Contracts > Problem: Which contracts does a class support? > Solution: Look for common programming idioms — Look for “key methods” – – – — — Intention-revealing names Key parameter types Recurring parameter types represent temporary associations Look for constructor calls Look for Template/Hook methods Look for super calls Use your tools! © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 32

Object-Oriented Reengineering Patterns Learn from the Past > Problem: How did the system get the way it is? > Solution: Compare versions to discover where code was removed — Removed functionality is a sign of design evolution — Use or develop appropriate tools — Look for signs of: – – Unstable design — repeated growth and refactoring Mature design — growth, refactoring and stability © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 33

Object-Oriented Reengineering Patterns Conclusion > Setting Direction + First Contact — First Project Plan > Initial Understanding + Detailed Model Capture — Plan the work … and Work the plan — Frequent and Short Iterations > Issues — scale — speed vs. accuracy — politics © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 34

Object-Oriented Reengineering Patterns Tests: Your Life Insurance © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 35

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

Object-Oriented Reengineering Patterns What and Why ? Definitions > Restructuring refers to transforming a system from one representation to another while remaining at the same abstraction level. — Chikofsky & Cross, ’ 90 > Refactoring is the process of changing a software system in such a way that it does not alter the external behaviour of the code, yet improves its internal structure — Fowler, ’ 99 Motivation > Alter the source-code to — solve problems identified earlier — without introducing new defects — and while the system remains in operation © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 37

Object-Oriented Reengineering Patterns The Reengineering Life-Cycle (0) requirement analysis Requirements (2) problem detection Designs (1) model capture Code (3) problem resolution (4) program transformation issues • reliability • time • risk (4) program transformation © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 38

Object-Oriented Reengineering Patterns Forces — Testing > > > Many legacy systems don’t have tests Software changes introduce new bugs You can’t test everything Concurrency and user interfaces are hard to test Testing is usually everyone’s lowest priority Knowledge concentration poses high risk Customers pay for features, not tests Customers don’t want buggy systems Good programmers don’t need tests New tools and techniques are more fun than testing Testing is akin to street-cleaning © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 39

Object-Oriented Reengineering Patterns Tests: Your Life Insurance Write Tests to Enable Evolution Use a Testing Framework Managing tests Grow Your Test Base Incrementally Write Tests to Understand Designing tests Record Business Test the Interface, Rules as Tests Not the Implementation • Test Fuzzy features • Test Old Bugs • Retest Persistent Problems Regression Test after Every Change © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz Migration Strategies 40

Object-Oriented Reengineering Patterns Write Tests to Enable Evolution Problem: How do you minimize the risks of change? Solution: Introduce automated, repeatable, stored tests Long-term evolution System documentation System Confidence Architectural evolution Turnover Risk minimization Confidence in Change Automated Tests Automated tests are the foundation of reengineering © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 41

Object-Oriented Reengineering Patterns Grow Your Test Base Incrementally > Problem: When can you stop writing tests? > Solution: When your tests cover all the code! — … however – – – you're paid to reengineer, not to write tests testing ALL the code is impossible design documentation is out-of date » semi-automated black-box testing is not an option Answer: Grow Your Test Base Incrementally • first test critical components (business value; likely to change; …) • keep a snapshot of old system (run new tests against old system) • focus on business values • test old bugs + new bugs that are reported © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 42

Object-Oriented Reengineering Patterns Use a Testing Framework > Problem: How do you encourage systematic testing? > Solution: Use a framework to structure your tests © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 43

Object-Oriented Reengineering Patterns Running tests © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 44

Object-Oriented Reengineering Patterns Test the Interface, Not the Implementation > Problem: How do you protect your investment in tests? > Solution: Apply black-box testing — Test interfaces, not implementations – Be sure to exercise the boundaries — Test scenarios, not paths – Use tools to check for coverage — Beware; – Enabling testing will influence your design! © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 45

Object-Oriented Reengineering Patterns Write Tests to Understand > Problem: How to decipher code without adequate tests or documentation? > Solution: Encode your hypotheses as test cases — Exercise the code — Formalize your reverse-engineering hypotheses — Develop tests as a by-product © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 46

Object-Oriented Reengineering Patterns Record Business Rules as Tests > Problem: How do you keep your system in sync with the business rules it implements? > A Solution: Good documentation + Good design — … however – – – business rules are too complex to design well documentation & design degrades when the rules change business rules become implicit in code and minds Solution: Record Business Rules as Tests - canonical examples exist - can be turned into input/output tests © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 47

Object-Oriented Reengineering Patterns Example: Payroll Business Rule > A person or couple gets an amount of money for every child he, she or they raise. Basically parents get CHF 150, - per month for every child younger than 12 years, and CHF 180, - for every child between 12 and 18 and for every child between 18 and 25 as long as the child is not working and is still in the educational system. A single parent gets the full 100% of this money as long as he or she is working more than 50%. Couples get a percentage of the money that is equal to the summed working percentages of both partners. © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 48

Object-Oriented Reengineering Patterns Example: Payroll Test Case "--- input-cases are extracted from a database" single. Person 80 With. One. Kid. Of 5 : = extract. . couple. Person 40 occupation. With. One. Kid. Of 5 : = extract. . couple. Person 100 occupation. With. One. Kid. Of 5 : = extract. . couple. Person. With. One. Kid. Of 14 : = extract. . "--- tests compare expected output against actual output" self assert: single. Person 80 occupation. With. One. Kid. Of 5 money. For. Kid = 150. self assert: couple. Person 40 occupation. With. One. Kid. Of 5 money. For. Kid = 150*4. self assert: couple. Person 100 occupation. With 2 Kids. Of 5 money. For. Kid = 150*2. self assert: couple. Person. With. One. Kid. Of 14 money. For. Kid = 180. © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 49

Object-Oriented Reengineering Patterns Other patterns > Retest Persistent Problems — Always tests these, even if you are making no changes to this part of the system > Test Fuzzy Features — Identify and write tests for ambiguous or ill-defined parts of the system > Test Old Bugs — Examine old problems reports, especially since the last stable release — De. Lano and Rising, 1998 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 50

Object-Oriented Reengineering Patterns Migration Strategies © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 51

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

Object-Oriented Reengineering Patterns The Reengineering Life-Cycle (0) requirement analysis Requirements (2) problem detection Designs (1) model capture Code (3) problem resolution (4) program transformation issues • reliability • time • risk (4) program transformation © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 53

Object-Oriented Reengineering Patterns Forces — Migration > Big-bang migration often fails > Users hate change > You need constant feedback to stay on track > Users just want to get their work done > The legacy data must be available during the transition © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 54

Object-Oriented Reengineering Patterns Migration Involve the Users Why Where to Prototype the Target Solution Always Have a Running Version Regression Test after Every Change Build Confidence How Migrate Systems Incrementally Why How Make a Bridge to the New Town Tests, your Life-Insurance © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz Conserve Familiarity Use Profiler before Optimizing Present the Right Interface Deprecate Obsolete Interfaces Distinguish Public from Published Interfaces 55

Object-Oriented Reengineering Patterns Involve the Users > Problem: How to get users to accept change? > Solution: Get them involved by giving them what they want — Start with the Most Valuable First — Prototypes can help raise enthusiasm, but may also raise expectations too high — Deploy early to increase commitment – – Diverts energy from development Pays back in quality feedback © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 56

Object-Oriented Reengineering Patterns Build Confidence > Problem: How do you overcome skepticism? > Solution: Deliver results in short, regular intervals — — Requires time to sync with users Requires effort to support the changes Requires care not to alienate original developers Requires regular, big demos to convince management © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 57

Object-Oriented Reengineering Patterns Migrate Systems Incrementally > Problem: When should you deploy the new system? > Solution: As soon as possible — — — Decompose the legacy system into parts Tackle one part at a time (Most Valuable First) Put suitable tests in place Decide whether to wrap, reengineer, or replace Deploy, support and obtain feedback Iterate © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 58

Object-Oriented Reengineering Patterns Prototype the Target Solution > Problem: How do you evaluate the target solution? > Solution: Develop a prototype — Evaluate the technical risks – – – New system architecture Migrating legacy data Adequate performance … — Exploratory prototype? – Explore specific issues, then throw it away! — Evolutionary prototype? – – Capture new architecture Migrate, wrap, reengineer or reimplement parts © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 59

Object-Oriented Reengineering Patterns Always Have a Running Version > Problem: Maintaining confidence during development > Solution: Integrate changes on a daily basis — Use version and configuration management tools — Maintain exhaustive regression tests where needed — Plan short iterations — Continuous Integration – If necessary, re-architect the system to enable short build times © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 60

Object-Oriented Reengineering Patterns Regression Test After Every Change > Problem: Making sure changes don’t break the system > Solution: Run the regression tests at each “stable” point — You must relentlessly write tests! – – Write new tests whenever new (untested) bugs are discovered Take time to convince your team of the Joy of Testing — If testing takes too long, categorize tests – But run all the tests at least once a day — Consider writing tests up front — Remember to Retest Persistent Problems © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 61

Object-Oriented Reengineering Patterns Make a Bridge to the New Town New System > Problem: How to migrate data? > Solution: Convert the underlying files/databases/… 1: read() 2: write() Bridge 1. 1: read() —. . . however – – – Legacy and new system must work in tandem Too much data; too many unknown dependencies Data is manipulated by components © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz Legacy System 1. 2: write() 2. 1: write() Data Store 62

Object-Oriented Reengineering Patterns Present the Right Interface > Problem: How do you prevent the legacy design from polluting the new system? > Solution: Wrap old services as new abstractions — Identify the new abstractions you want — Wrap the legacy services to emulate the new interface – – Avoid directly accessing old procedural interfaces Avoid wrapping as pseudo-OO «utility» classes © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 63

Object-Oriented Reengineering Patterns Public vs. Published Interface > Problem: How to design interface for target solution? > Solution? : Think deeply —. . . however – – – Enable migration to target system ASAP Avoid freezing the interface of target component Costly ripple-effects of changes to public interface Solution: Distinguish between “public” and “published” interface • public = stable target interface • published = available, but unstable (use at your own risk) • language features (protected, friends, …) • naming conventions © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 64

Object-Oriented Reengineering Patterns Deprecate Obsolete Interfaces > Problem: How to modify an interface without invalidating all clients? > Solution: Flag the old interface as «deprecated» — Old and new interfaces can co-exist for a time – Deprecated usage can be lazily patched — Various techniques possible – – – Documentation (easy to ignore) Move or rename old interfaces (painful) Add warnings to deprecated code (should be non-intrusive) © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 65

Object-Oriented Reengineering Patterns Conserve Familiarity > Problem: How to avoid disrupting Users’ work? > Solution: Avoid radical changes — Avoid alienating users — Introduce a constant, small number of changes with each release © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 66

Object-Oriented Reengineering Patterns Use Profiler Before Optimizing > Problem: When should you rewrite inefficient code? > Solution: Only when you have benchmarks that prove it is worthwhile — “Do it, then do it right, then do it fast” © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 67

Object-Oriented Reengineering Patterns Conclusion > Avoid risk — small increments (“chicken little”) — develop suite of regression tests > … at acceptable cost — Migration costs as much as new development ! — But you avoid "hidden costs" – – team morale in maintenance team satisfying two customer bases © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 68

Object-Oriented Reengineering Patterns Detecting Duplicated Code © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 69

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

Object-Oriented Reengineering Patterns Detecting Duplicated Code Detect Compare Code Mechanically Understand Visualize Code as Dotplots Redistribute Responsibilities © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz Transform Conditionals to Polymorphism 71

Object-Oriented Reengineering Patterns Duploc: Detecting Duplicated Code Compare, visualize and navigate multiple source files © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 72

Object-Oriented Reengineering Patterns Pattern: Visualize Code as Dotplots Problem — How can you effectively identify significant duplication in a complex software system? Solution — Visualize the code as a dotplot, where dots represent duplication. Steps — Normalize the source files — Compare files line-by-line — Visualize and interpret the dotplots © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 73

Object-Oriented Reengineering Patterns Clone detection by string-matching Solid diagonals indicate significant duplication between or within source files. © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 74

Object-Oriented Reengineering Patterns Dotplot Visualization Sample Dot Configurations: © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 75

Object-Oriented Reengineering Patterns Redistribute Responsibilities © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 76

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

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

Object-Oriented Reengineering Patterns Redistribute Responsibilities Chains of data containers Monster client of data containers Split Up God Class Eliminate Navigation Code Data containers Move Behaviour Close to Data © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 79

Object-Oriented Reengineering Patterns The Law of Demeter > A method M of an object O should invoke only the methods of the following kinds of objects: 1. 2. 3. 4. O itself parameters of M any object M creates /instantiates direct component objects of O http: //en. wikipedia. org/wiki/Law_of_Demeter © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 80

Object-Oriented Reengineering Patterns The Core of the Problem Indirect Provider do. Something() provider Immediate Provider +provider get. Provider() intermediate Indirect Client intermediate. provider. do. Something() Or intermediate. get. Provider. do. Something() © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 81

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 82

Object-Oriented Reengineering Patterns Transformation… Provider +x +y +sety(val) +setx(val) Provider -x -y -sety(val) +bump() Client Op 2() … provider. sety(provider. x + provider. y) … Client Op 2() … provider. bump() … this. sety(provider. x + provider. y) © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 83

Object-Oriented Reengineering Patterns Detection strategy > Look for data containers — Classes with only accessors > Duplicated client code > Methods using sequence of accessors © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 84

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 85

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 86

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 87

Object-Oriented Reengineering Patterns Eliminate Navigation Code Carburetor +fuel. Valve. Open Engine +carburetor Car -engine +increase. Speed() … engine. carburetor. fuel. Valve. Open = true Carburetor +fuel. Valve. Open Engine -carburetor +speed. Up() Car -engine +increase. Speed() … engine. speed. Up() carburetor. fuel. Valve. Open = true Carburetor -fuel. Valve. Open +open. Fuel. Valve() fuel. Valve. Open = true Engine -carburetor +speed. Up() Car -engine +increase. Speed() carburetor. open. Fuel. Valve() © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 88

Object-Oriented Reengineering Patterns 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 — an. Object. m 1(). m 2(). op() identified by – egrep ‘. *(). ’ *. java © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 89

Object-Oriented Reengineering Patterns Detection (ii) > Not a problem — (a. is. Node()) & (a. is. Abstract()) > Beware of disguised navigation Token token; token = parse. Tree. token(); if (token. identifier() != null){… if(parse. Tree. token(). identifier() != null){… © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 90

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 91

Object-Oriented Reengineering Patterns Law of Demeter’s Dark Side > Can produce large interfaces Class A inst. Var: my. Collection A>>do: a. Block my. Collection do: a. Block A>>collect: a. Block my. Collection collect: a. Block … … … © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 92

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 93

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 94

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 95

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 96

Object-Oriented Reengineering Patterns Transform Conditionals to Polymorphism © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 97

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

Object-Oriented Reengineering Patterns Transform Conditionals to Polymorphism Test provider type Test external attribute Test self type Transform Client Type Checks Transform Self Type Checks Transform Conditionals into Registration Test object state Test null values Introduce Null Object © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz Factor Out Strategy Factor Out State 99

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 100

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 101

Object-Oriented Reengineering Patterns Transform Self Type Checks Client A m() … case Text: this. do. Something() case Border: this. do. Other() case D: > Symptoms — Simple extensions require many changes in conditional code — Subclassing impossible without duplicating and updating conditional code — Adding new case to conditional code © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 102

Object-Oriented Reengineering Patterns Transformation Client A m() … case Text: this. do. Something() case Border: case D: A m() hook() Client this. do. Something() Text hook() © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz Border hook() … this. hook() D hook() 103

Object-Oriented Reengineering Patterns Example: Transform Self Type Checks class Message { private: int type_; void* data; . . . void send (Channel* ch) { switch (type_) { case TEXT : { ch->next. Put. All(data); break; } case ACTION : { ch->do. Action(data); . . . © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 104

Object-Oriented Reengineering Patterns Transform Self Type Check Client 1 Message send() switch (type_) { case TEXT : { ch->next. Put. All(data); break; } case ACTION : { ch->do. Action(data); . . . Client 2 Client 1 Message send() Action. Message send() © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz Client 2 Text. Message send() 105

Object-Oriented Reengineering Patterns 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’ © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 106

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 107

Object-Oriented Reengineering Patterns Transform Client Type Checks 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() B x() C init() Y() D z() > Symptoms — Clients perform explicit type checks / type coercions — Adding a new provider (A subclass) change all clients — Clients are defining logic about providers © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 108

Object-Oriented Reengineering Patterns Transformation switch (a. class) case B: a. init(); ((B) a). x(); case C: a. init(); ((C)) a). y(); Case D: ((D) a). z() a. doit(); this. init (); this. x(); this. init (); this. y(); Client a: A m() B x() doit() © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz A init() Client a: A m() A init() doit() C init() Y() doit() B x() C init() Y() D z() doit() this. z(); 109
![Object-Oriented Reengineering Patterns Example: Transform Client Type Checks void make. Calls (Telephone* phone. Array[]) Object-Oriented Reengineering Patterns Example: Transform Client Type Checks void make. Calls (Telephone* phone. Array[])](http://slidetodoc.com/presentation_image_h2/06708de52b2f05c042c9c1d3eb677386/image-110.jpg)
Object-Oriented Reengineering Patterns Example: Transform Client Type Checks void make. Calls (Telephone* phone. Array[]) { for (Telephone *p = phone. Array; p; p++) { switch (p-> phone. Type()) { case TELEPHONE: : POTS : { POTSPhone* potsp = (POTSPhone*)p potsp->tourne(); potsp->call(); . . . case TELEPHONE: : ISDN : { ISDNPhone* isdnp = (ISDNPhone*)p isdnp->init. Line(); isdnp->connect(); . . . © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 110

Object-Oriented Reengineering Patterns Transform Client Type Check Telephone. Box make. Call () Telephone POTSPhone. . . ISDNPhone. . . Telephone. Box make. Call () Telephone make. Call() POTSPhone make. Call(). . . © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz ISDNPhone make. Call. . . 111

Object-Oriented Reengineering Patterns Detection > Transform Self Type Checks > Changing clients of method when new case added > Attribute representing a type In Smalltalk: is. Kind. Of: , is. Member. Of: > In Java: instanceof > x. get. Class() == y. get. Class() > x. get. Class(). get. Name(). equals(…. ) © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 112

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 113

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 114

Object-Oriented Reengineering Patterns Factor Out Strategy > Problem: — How do you make a class whose behavior depends on testing certain value more extensible? > Solution: — Apply State Pattern — Encapsulate the behavior and delegate using a polymorphic call © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 115

Object-Oriented Reengineering Patterns Transformation A operation() … case X: … case Z: …. … A operation() strategy Abstract. Strategy handle. Operation() … strategy. handle. Operation() … Strategy. X handle. Operation() Strategy. Z handle. Operation() © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 116

Object-Oriented Reengineering Patterns Pros/Cons/Difficulties > Pros — Behavior extension is well-identified — Behavior using the extension is clearer — Change behavior at run-time > Cons — Namespace get cluterred — Yet another indirection > Difficulties — Behavior can be difficult to convert and encapsulate (passing parameter…) © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 117

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 118

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 119

Object-Oriented Reengineering Patterns Transformation (i) Tool. Client read() XMLReader open. File (File) Word. Reader on (file) suffix : = selected. File suffix = ‘xml’ if. True: [ XMLReader open. File: selected. File. ^ self] suffix = ‘doc’ if. True: [Word. Reader on: selected. File. ^ self]. … © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 120

Object-Oriented Reengineering Patterns Transformation (ii) Tool. Client read() (Plugin. Manager unique. Instance find. Tool. For: selected. File suffix) action Plugin. Manager add/remove (Tool) find. Tool. For (String) Plugin action for: String use: class with: method (Plugin. Manager unique. Instance add: (Plugin for: ‘xml’ use: XMLReader with: open. File) (Plugin. Manager unique. Instance remove: (Plugin for: ‘xml’ use: XMLReader with: open. File) © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz XMLReader open. File (File) load() unload() Word. Reader on (file) load() unload() 121

Object-Oriented Reengineering Patterns 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) © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 122

Object-Oriented Reengineering Patterns Introduce Null. Object > 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 123

Object-Oriented Reengineering Patterns Transformation Client m() Real. Object doit() … if (a!=Null) { a. doit()} … Abstract. Object doit() Client m() … a. doit() … Real. Object doit() Null. Object doit() nothing © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 124

Object-Oriented Reengineering Patterns Pros/Cons/Discussions > Pros — Clients do not need to test for null values > Difficulties — Different clients may have different null behavior — In strongly typed languages, you have to introduce Null interface > Hint — The Null. Object does not have to be a subclass of Real. Object superclass as soon as it implements Real. Object’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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 125

Object-Oriented Reengineering Patterns 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 © Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz 126
- Slides: 126