Maintainable Software Software Engineering Andreas Zeller, - - PowerPoint PPT Presentation

maintainable software
SMART_READER_LITE
LIVE PREVIEW

Maintainable Software Software Engineering Andreas Zeller, - - PowerPoint PPT Presentation

Maintainable Software Software Engineering Andreas Zeller, Saarland University The Challenge Software may live much longer than expected Software must be continuously adapted to a changing environment Maintenance takes 5080%


slide-1
SLIDE 1

Maintainable Software

Software Engineering
 Andreas Zeller, Saarland University

slide-2
SLIDE 2

The Challenge

  • Software may live much longer than

expected

  • Software must be continuously adapted to

a changing environment

  • Maintenance takes 50–80% of the cost
  • Goal: Make software maintainable and

reusable – at little or no cost

slide-3
SLIDE 3

Software Maintenance

  • Corrective maintenance: Fix bugs
  • Adaptive maintenance: adapt system

to the environment it operates

  • Perfective maintenance: adapt to new
  • r changed requirements
  • Preventive maintenance: increase

quality or prevent future bugs from

slide-4
SLIDE 4

Software Maintenance

  • Follow principles of object-oriented

design

  • Follow guidelines for maintainable

software

slide-5
SLIDE 5

Principles


  • f object-oriented design
  • Abstraction
  • Encapsulation
  • Modularity
  • Hierarchy

Goal: Maintainability and Reusability

slide-6
SLIDE 6

Principles


  • f object-oriented design
  • Abstraction
  • Encapsulation
  • Modularity
  • Hierarchy
slide-7
SLIDE 7

Abstraction

Concrete Object General Principle

slide-8
SLIDE 8

Abstraction…

  • Highlights common properties of objects
  • Distinguishes important and unimportant

properties

  • Must be understood even without a

concrete object

slide-9
SLIDE 9

Abstraction

“An abstraction denotes the essential characteristics of an object that distinguish it from all other kinds of objects and thus provide crisply defined conceptual boundaries, relative to the perspective of the viewer”

slide-10
SLIDE 10

Perspectives

slide-11
SLIDE 11

Example: Sensors

slide-12
SLIDE 12

An Engineer’s Solution

void check_temperature() { // see specs AEG sensor type 700, pp. 53 short *sensor = 0x80004000; short *low = sensor[0x20]; short *high = sensor[0x21]; int temp_celsius = low + high * 256; if (temp_celsius > 50) { turn_heating_off() } }

slide-13
SLIDE 13

Abstract Solution

typedef float Temperature; typedef int Location; class TemperatureSensor { public: TemperatureSensor(Location); ~TemperatureSensor(); void calibrate(Temperature actual); Temperature currentTemperature() const; Location location() const; private: … }

All implementation details are hidden

slide-14
SLIDE 14

More Abstraction

slide-15
SLIDE 15

Principles


  • f object-oriented design
  • Abstraction – hide details
  • Encapsulation
  • Modularity
  • Hierarchy
slide-16
SLIDE 16

Principles


  • f object-oriented design
  • Abstraction – Hide details
  • Encapsulation
  • Modularity
  • Hierarchy
slide-17
SLIDE 17

Encapsulation

  • No part of a complex system should

depend on internal details of another

  • Goal: keep software changes local
  • Information hiding: Internal details 


(state, structure, behavior) become the

  • bject’s secret
slide-18
SLIDE 18

Encapsulation

“Encapsulation is the process of compartmentalizing the elements of an abstraction that constitute its structure and its behavior; encapsulation serves to separate the contractual interface of an abstraction and its implementation.”

slide-19
SLIDE 19

Encapsulation

slide-20
SLIDE 20

An active Sensor

class ActiveSensor { public: ActiveSensor(Location) ~ActiveSensor(); void calibrate(Temperature actual); Temperature currentTemperature() const; Location location() const; void register(void (*callback)(ActiveSensor *)); private: … }

called when temperature changes

Callback management is the sensor’s secret

slide-21
SLIDE 21

Anticipating Change

Features that are anticipated to change should be isolated in specific components

  • Number literals
  • String literals
  • Presentation and interaction
slide-22
SLIDE 22

Number literals

int a[100]; for (int i = 0; i <= 99; i++) a[i] = 0; const int SIZE = 100; int a[SIZE]; for (int i = 0; i < SIZE; i++) a[i] = 0; const int ONE_HUNDRED = 100; int a[ONE_HUNDRED]; …

slide-23
SLIDE 23

Number literals

double sales_price = net_price * 1.19; final double VAT = 1.19; double sales_price = net_price * VAT;

slide-24
SLIDE 24

String literals

if (sensor.temperature() > 100) printf(“Water is boiling!”); if (sensor.temperature() > BOILING_POINT) printf(message(BOILING_WARNING, “Water is boiling!”); if (sensor.temperature() > BOILING_POINT) alarm.handle_boiling();

slide-25
SLIDE 25

Principles


  • f object-oriented design
  • Abstraction – Hide details
  • Encapsulation – Keep changes local
  • Modularity
  • Hierarchy
slide-26
SLIDE 26

Principles


  • f object-oriented design
  • Abstraction – Hide details
  • Encapsulation – Keep changes local
  • Modularity
  • Hierarchy
slide-27
SLIDE 27

Modularity

  • Basic idea: Partition a system such that

parts can be designed and revised independently (“divide and conquer”)

  • System is partitioned into modules that

each fulfil a specific task

  • Modules should be changeable and

reuseable independent of other modules

slide-28
SLIDE 28

Modularity

slide-29
SLIDE 29

Modularity

“Modularity is the property of a system that has been decomposed into a set of cohesive and loosely coupled modules.”

slide-30
SLIDE 30

Module Balance

  • Goal 1: Modules should hide information –

and expose as little as possible

  • Goal 2: Modules should cooperate –


and therefore must exchange information

  • These goals are in conflict with each other
slide-31
SLIDE 31

Principles of Modularity

  • High cohesion – Modules should contain

functions that logically belong together

  • Weak coupling – Changes to modules

should not affect other modules

  • Law of Demeter – talk only to friends
slide-32
SLIDE 32

High cohesion

  • Modules should contain functions that

logically belong together

  • Achieved by grouping functions that work on

the same data

  • “natural” grouping in object oriented design
slide-33
SLIDE 33

Weak coupling

  • Changes in modules should not impact
  • ther modules
  • Achieved via
  • Information hiding
  • Depending on as few modules as possible
slide-34
SLIDE 34

Law of Demeter

  • r Principle of Least Knowledge
  • Basic idea: Assume as little as

possible about other modules

  • Approach: Restrict method

calls to friends

slide-35
SLIDE 35

Call your Friends

A method M of an object O should only call methods of

  • 1. O itself
  • 2. M’s parameters
  • 3. any objects created in M
  • 4. O’s direct component objects

“single dot rule”

slide-36
SLIDE 36

Demeter: Example

class Uni { Prof boring = new Prof(); public Prof getProf() { return boring; } public Prof getNewProf() { return new Prof(); } } class Test { Uni uds = new Uni(); public void one() { uds.getProf().fired(); } public void two() { uds.getNewProf().hired(); } }

slide-37
SLIDE 37

Demeter: Example

class Uni { Prof boring = new Prof(); public Prof getProf() { return boring; } public Prof getNewProf() { return new Prof(); } public void fireProf(...) { ... } } class BetterTest { Uni uds = new Uni(); public void betterOne() { uds.fireProf(...); } }

slide-38
SLIDE 38

Demeter effects

  • Reduces coupling between modules
  • Disallow direct access to parts
  • Limit the number of accessible classes
  • Reduce dependencies
  • Results in several new wrapper methods

(“Demeter transmogrifiers”)

slide-39
SLIDE 39

Principles


  • f object-oriented design
  • Abstraction – Hide details
  • Encapsulation – Keep changes local
  • Modularity – Control information flow


High cohesion • weak coupling • talk only to friends

  • Hierarchy
slide-40
SLIDE 40

Principles


  • f object-oriented design
  • Abstraction – Hide details
  • Encapsulation – Keep changes local
  • Modularity – Control information flow


High cohesion • weak coupling • talk only to friends

  • Hierarchy
slide-41
SLIDE 41

Hierarchy

“Hierarchy is a ranking or ordering

  • f abstractions.”
slide-42
SLIDE 42

Central Hierarchies

  • “has-a” hierarchy –


Aggregation of abstractions

  • A car has three to four wheels
  • “is-a” hierarchy –


Generalization across abstractions

  • An ActiveSensor is a TemperatureSensor
slide-43
SLIDE 43

Central Hierarchies

  • “has-a” hierarchy –


Aggregation of abstractions

  • A car has three to four wheels
  • “is-a” hierarchy –


Generalization across abstractions

  • An ActiveSensor is a TemperatureSensor
slide-44
SLIDE 44

Hierarchy principles

  • Open/Close principle – Classes should be
  • pen for extensions
  • Liskov principle – Subclasses should not

require more, and not deliver less

  • Dependency principle – Classes should
  • nly depend on abstractions
slide-45
SLIDE 45

Hierarchy principles

  • Open/Close principle – Classes should be
  • pen for extensions
  • Liskov principle – Subclasses should not

require more, and not deliver less

  • Dependency principle – Classes should
  • nly depend on abstractions
slide-46
SLIDE 46

Open/Close principle

  • A class should be open for extension,


but closed for changes

  • Achieved via inheritance and dynamic binding
slide-47
SLIDE 47

An Internet Connection

void connect() { if (connection_type == MODEM_56K) { Modem modem = new Modem(); modem.connect(); } else if (connection_type == ETHERNET) … else if (connection_type == WLAN) … else if (connection_type == UMTS) … }

slide-48
SLIDE 48

Solution with Hierarchies

MyApp connect() Connection connect() hangup() ModemConnection connect() hangup() WLANConnection connect() hangup() EthernetConnection connect() hangup()

slide-49
SLIDE 49

enum { FUN50, FUN120, FUN240, ... } plan; enum { STUDENT, ADAC, ADAC_AND_STUDENT ... } special; enum { PRIVATE, BUSINESS, ... } customer_type; enum { T60_1, T60_60, T30_1, ... } billing_increment; int compute_bill(int seconds) { if (customer_type == BUSINESS) billing_increment = T1_1; else if (plan == FUN50 || plan == FUN120) billing_increment = T60_1; else if (plan == FUN240 && contract_year < 2011) billing_increment = T30_1; else billing_increment = T60_60; if (contract_year >= 2011 && special != ADAC) billing_increment = T60_60; // etc.etc.

slide-50
SLIDE 50

Hierarchy Solution

  • You can add a new plan at any time!

Fun50 Fun120 + percentage() Discount ADAC Student + units(seconds) BillingIncrement T30_1 T1_1 – year Plan 1 0..*

slide-51
SLIDE 51

Hierarchy principles

  • Open/Close principle – Classes should be
  • pen for extensions
  • Liskov principle – Subclasses should not

require more, and not deliver less

  • Dependency principle – Classes should
  • nly depend on abstractions
slide-52
SLIDE 52

Liskov Substitution Principle

  • An object of a superclass should always be

substitutable by an object of a subclass:

  • Same or weaker preconditions
  • Same or stronger postconditions
  • Derived methods should not assume more
  • r deliver less
slide-53
SLIDE 53

Circle vs Ellipse

  • Every circle is an

ellipse

  • Does this hierarchy

make sense?

  • No, as a circle

requires more and delivers less

Circle draw() Ellipse draw()

slide-54
SLIDE 54

Hierarchy principles

  • Open/Close principle – Classes should be
  • pen for extensions
  • Liskov principle – Subclasses should not

require more, and not deliver less

  • Dependency principle – Classes should
  • nly depend on abstractions
slide-55
SLIDE 55

Dependency principle

  • A class should only depend on abstractions

– never on concrete subclasses
 (dependency inversion principle)

  • This principle can be used to break

dependencies

slide-56
SLIDE 56

Dependency

// Print current Web page to FILENAME. void print_to_file(string filename) { if (path_exists(filename)) { // FILENAME exists; // ask user to confirm overwrite bool confirmed = confirm_loss(filename); if (!confirmed) return; } // Proceed printing to FILENAME ... }

slide-57
SLIDE 57

Cyclic Dependency

Constructing, testing, reusing individual modules becomes impossible!

Core

+print_to_file()

UserPresentation

+confirm_loss() invokes invokes

slide-58
SLIDE 58

Dependency

// Print current Web page to FILENAME. void print_to_file(string filename, Presentation *p) { if (path_exists(filename)) { // FILENAME exists; // ask user to confirm overwrite bool confirmed = p->confirm_loss(filename); if (!confirmed) return; } // Proceed printing to FILENAME ... }

slide-59
SLIDE 59

Depending on
 Abstraction

Core

+print_to_file()

Presentation

+confirm_loss()

UserPresentation

+confirm_loss()

AutomatedPresentation

+confirm_loss() return true; ask user

slide-60
SLIDE 60

Choosing Abstraction

  • Which is the

“dominant” abstraction?

  • How does this

choice impact the remaining system?

slide-61
SLIDE 61

Hierarchy principles

  • Open/Close principle – Classes should be
  • pen for extensions
  • Liskov principle – Subclasses should not

require more, and not deliver less

  • Dependency principle – Classes should
  • nly depend on abstractions
slide-62
SLIDE 62

Principles


  • f object-oriented design
  • Abstraction – Hide details
  • Encapsulation – Keep changes local
  • Modularity – Control information flow


High cohesion • weak coupling • talk only to friends

  • Hierarchy – Order abstractions 


Classes open for extensions, closed for changes • Subclasses that do not require more or deliver less • depend only on abstractions

slide-63
SLIDE 63

Principles


  • f object-oriented design
  • Abstraction – Hide details
  • Encapsulation – Keep changes local
  • Modularity – Control information flow


High cohesion • weak coupling • talk only to friends

  • Hierarchy – Order abstractions


Classes open for extensions, closed for changes • Subclasses that do not require more or deliver less • depend only on abstractions

Goal: Maintainability and Reusability

slide-64
SLIDE 64

Software Maintenance

  • Follow principles of object-oriented

design

  • Follow guidelines for maintainable

software

slide-65
SLIDE 65

Software Maintenance

  • Follow guidelines


for maintainable
 software

slide-66
SLIDE 66
  • 1. Write Short Units of

Code

  • Limit the length of code units


to ~15 lines of code

  • Split long units into multiple smaller

units

  • Smaller units are easier to

understand, easier to test, easier to reuse

slide-67
SLIDE 67

Write Short Units of Code

public void doIt() {
 // Query amount from database results = executeQuery("SELECT account from "...); if (results == null) ... // Find account numbers
 while (results.next()) { ... whatever ... } // Store accounts in database again ... }

slide-68
SLIDE 68

Write Short Units of Code

public void doIt() {
 results = queryAmounts(); accounts = findAccountNumbers(results);
 storeAccounts(accounts); }

slide-69
SLIDE 69
  • 2. Write Simple Units of

Code

  • Limit the number of branches to 4
  • Split complex units into simpler
  • nes; avoiding complex units

altogether

  • Makes units easier to modify and test
slide-70
SLIDE 70

Write Simple Units of Code

public List<Color> getFlagColors(Nationality nat) { List<color> result; switch (nat) { case DUTCH: result = Arrays.asList(RED, WHITE, BLUE); break; case GERMAN: result = Arrays.asList(BLACK, RED, GOLD); break; case FRENCH: result = Arrays.asList(BLUE, WHITE, RED); break; // ...
 }

slide-71
SLIDE 71

Write Simple Units of Code

public class GermanNationality extends Nationality {
 public List<Color> getFlagColors() { return Array.asList(BLACK, RED, GOLD); } }

slide-72
SLIDE 72
  • 3. Write Code Once
  • Do not copy code
  • Write reusable, generic code and/or

call existing methods

  • When code is copied, bugs need to be

fixed at multiple places

  • Instead, introduce delegate method

and/or superclass

slide-73
SLIDE 73

Write Code Once

public class CheckingAccount { public Transfer makeTransfer(String account, Money amount) { // Check account number
 int sum = 0; for (int i = 0; i < account.length(); i++) sum += (9 - i) * convert(account.charAt(i)); if (sum % 11 != 0) throw BusinessException("Invalid Account"); // ... } } public class SavingsAccount { public Transfer makeTransfer(String account, Money amount) { // Check account number
 int sum = 0; for (int i = 0; i < account.length(); i++) sum += (9 - i) * convert(account.charAt(i)); if (sum % 11 != 0) throw BusinessException("Invalid Account"); // ... } }

slide-74
SLIDE 74

Write Code Once

public class CheckingAccount extends Account { public Transfer makeTransfer(String account, Money amount) { validateAccount();
 // ... } } public class SavingsAccount extends Account { public Transfer makeTransfer(String account, Money amount) { validateAccount(); // ... } } public class Account { public void validateAccount(String account) { int sum = 0; for (int i = 0; i < account.length(); i++) sum += (9 - i) * convert(account.charAt(i)); if (sum % 11 != 0) throw BusinessException("Invalid Account"); } }

slide-75
SLIDE 75
  • 4. Keep Unit Interfaces

Small

  • Limit the number of parameters per unit

to 4

  • Do this by extracting parameters into
  • bjects
  • Keeping the number of parameters low

makes units easier to understand and reuse

slide-76
SLIDE 76

Keep Unit Interfaces Small

private void drawRectangle(Canvas c, Graphics g, int x, int y, int w, int h) class Rectangle {
 private final Point position; private final int width; private final int height; // ... private void render(Canvas c, Graphics g);
 // ...
 }

slide-77
SLIDE 77
  • 5. Separate Concerns


In Modules

  • Avoid large modules to achieve loose

coupling between them

  • Do this by
  • assigning responsibilities to separate

modules

  • hiding implementation details
  • Changes in a loosely coupled code base are

easier to oversee and execute

slide-78
SLIDE 78
  • 6. Couple

Architecture

  • Achieve loose coupling between top-

level components

  • Do this by minimizing the amount of

code that is exposed to modules in

  • ther components
  • Independent components ease

isolated maintenance

slide-79
SLIDE 79
  • 7. Keep Architecture

Components

  • Balance the number and relative size
  • f top-level components in your code
  • Do this by organizing the code in a

way that the number of components is ~9 (between 6–12) and that the components are of approximately equal size

  • Balanced components ease locating

code and allow for isolated

slide-80
SLIDE 80

Keep Architecture Components

changes existing component unbalanced balanced

slide-81
SLIDE 81
  • 8. Keep your Codebase Small
  • Keep your codebase as small as

possible

  • Do this by avoiding codebase growth

and actively reducing system size

  • A small product / project / team is a

success factor

slide-82
SLIDE 82
  • 9. Automate Tests
  • Automate tests for your codebase
  • Do this by writing automated tests

using a test framework

  • Automated testing makes development

predictable and less risky

slide-83
SLIDE 83
  • 10. Write Clean

Code

  • Write clean code.
  • Do this by not leaving code smells

behind after development work

  • Clean code is maintainable code
slide-84
SLIDE 84

Write Clean Code

  • Leave no bad comments behind


Whatever can be said in code needs no comment

  • Leave no code in comments behind


Fix it or reject it

  • Leave no magic numbers behind


USD = EUR * 1.09. What could ever change?

  • LeaveNoExcessivelyLongIdentifiersBehi

nd()

slide-85
SLIDE 85

Software Maintenance

  • Follow principles of object-oriented

design

  • Follow guidelines for maintainable

software