code and design smells. are they a real threat?

28
Code and design smells. Are they a real threat? Antoni Rasul

Upload: globallogic-ukraine

Post on 16-Apr-2017

663 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Code and Design Smells. Are They a Real Threat?

Code and design smells. Are they a real threat?

Antoni Rasul

Page 2: Code and Design Smells. Are They a Real Threat?

Lehman's laws of software evolution

• Originally formulated by Manny Lehman in 1974 and later refined, as a result of empirical study within IBM

• Aimed at finding scientific properties of evolution of different classes of software

• System types:

• S-type (specified) Systems

• P-type (problem solving) Systems

• E-type (evolutionary) Systems

Page 3: Code and Design Smells. Are They a Real Threat?

E-type Systems laws of evolution (since 1996)

I. Continuing Change

II. Increasing Complexity

III. Self Regulation

IV. Conservation of Organizational Stability

V. Conservation of Familiarity

VI. Continuing Growth

VII. Declining Quality

VIII.Feedback System

Page 4: Code and Design Smells. Are They a Real Threat?

Technical debt

• Monetary metaphor

• Impossible to measure precisely

• If not repaid regularly – accumulates interest

Time

Effort

Interest payment

Feature

Page 5: Code and Design Smells. Are They a Real Threat?

Warning signs of rising debt

• The quality of your software is degrading

• The bug backlog is increasing

• The bug threshold in release is rising

• Your productivity is slipping

• Fictional deadlines

• The team members don't care

Page 6: Code and Design Smells. Are They a Real Threat?

Effective ways to reduce the debt

• Define the debt

• Revise the Definition of Done

• Testing tasks part of story

• Done means code cleaned, refactored, deployed and tested

• Use tools like Sonar to visualise (also for management)

• Refactor

• Identify code smells and plan for change

• Automate

Page 7: Code and Design Smells. Are They a Real Threat?

Refactoring

• Refactoring: a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.

• Refactor when:

• Adding a function

• Fixing a bug

• Doing a code review

• Perform larger refactorings in small increments

Page 8: Code and Design Smells. Are They a Real Threat?

Code smells

• Duplicated Code

• Shotgun surgery

• Feature envy

• Switch statements

• Refused bequest

• Telescoping constructors

• Big Ball of Mud

Page 9: Code and Design Smells. Are They a Real Threat?

Techniques

• Extract method/class

• Substitute algorithm

• Introduce parameter object

• Replace type conditional with Polymorphism/Strategy

• Fluent interface with Essence pattern

Page 10: Code and Design Smells. Are They a Real Threat?

Duplicated Code or Structure

• In same class

Extract Method

• In sibling classes

Extract Method

Pull method Up

• In unrelated classes

Substitute algorithm

Extract Class

Use extracted class in other places

Page 11: Code and Design Smells. Are They a Real Threat?

Shotgun surgery

• Adding simple feature requires small changes in gazillion classes

• Difficult to eliminate in big systems

• Removing duplicates first – reduces the smell

• Code generation tools help reduce the problem further

Page 12: Code and Design Smells. Are They a Real Threat?

Feature envy

• One class’s method is extensively using data and methods

of another class

• Usually easy to spot and fix – move the method to the class

where it belongs

• If method uses several classes – move it to the one with

most used data

• Use Move Method, possibly with Extract Method

Page 13: Code and Design Smells. Are They a Real Threat?

Switch by type in multiple places - problem

public class PriceCalculator { public double calculatePrice(Customer customer, Car car, Period period) { double tax = 0; double discount = 0; switch(customer.getType()) { case REGULAR: discount = 0; tax = 0.2; case BUSINESS: discount = 0.5; tax = 0.2; case BIG_BUSINESS: discount = 0.2; tax = 0.1; } return period.getDays() * car.getDayPrice() * (1 - discount * tax); } }

Page 14: Code and Design Smells. Are They a Real Threat?

Switch by type - Replace type code by polymorphism

public interface Customer { CustomerType getType(); } public interface CustomerType { double getDiscount(); double getTax(); } public class RegularCustomer implements CustomerType { ... } public class BusinessCustomer implements CustomerType { ... } public class BigBusinessCustomer implements CustomerType { ... }

Page 15: Code and Design Smells. Are They a Real Threat?

Switch by type - refactored

public class PriceCalculator { public double calculatePrice(Customer customer, Car car, Period period) { final double tax = customer.getType().getTax(); final double discount = customer.getType().getDiscount(); return period.getDays() * car.getDayPrice() * (1 - discount * tax); } }

• If price calculation algorithm would depend on Customer type – use Strategy

Page 16: Code and Design Smells. Are They a Real Threat?

Refused bequest

public interface Car { String getModel(); double getDayPrice(); } public class Truck implements Car { public String getMaxCargoVolume(){...}; } public class CarFleet implements Car { private List<Car> cars; public String getModel() { return null; // Hmmmmm Let's return null here } public double getDayPrice() { return this.cars.stream().mapToDouble(c -> c.getDayPrice()).sum(); } }

• When subclass inherits data/methods that it doesn’t want

• When sublcass refuses to meaningfully implement all interface methods

Page 17: Code and Design Smells. Are They a Real Threat?

Refused bequest - ideas

• Hierarchy is wrong

• Reshuffle hierarchy, so that parent contains only what’s common

• Another solution – pull the violating class out of the hierarchy and use a delegate to the instance of (former) parent class

Page 18: Code and Design Smells. Are They a Real Threat?

Refused bequest – example refactored

public interface RentableItem { double getDayPrice(); } public interface Car extends RentableItem { String getModel(); } public interface CarFleet extends RentableItem { double getFleetDiscount(); } public class StandardFleet implements CarFleet { private List<Car> cars; public double getDayPrice() { return this.cars.stream().mapToDouble(c -> c.getDayPrice()).sum(); } public double getFleetDiscount() { return 0.1; } }

Page 19: Code and Design Smells. Are They a Real Threat?

Telescoping constructors

public class RentalContract { ... public RentalContract(Customer cust, List<Car> cars, Date startDate) {

this(cust, car, startDate, null); } public RentalContract(Customer cust, List<Car> cars, Date startDate, Date endDate) {

this(cust, cars, null, null, startDate, endDate); } public RentalContract(Customer customer, List<Car> cars, BigDecimal extraDiscount, String description, Date startDate, Date endDate) {

this.customer = customer; this.cars = cars; this.extraDiscount = extraDiscount; this.description = description; this.startDate = startDate; this.endDate = endDate;

} ...

}

Page 20: Code and Design Smells. Are They a Real Threat?

Telescoping constructors – required fields

public RentalContract( Customer customer, List<Car> cars, BigDecimal extraDiscount, String description, Date startDate, Date endDate) {...}

• Instantiating is cumbersome, error-prone

• Optional fields are mixed with required ones

Page 21: Code and Design Smells. Are They a Real Threat?

Telescoping constructors – refactoring ideas

• Introduce parameter object from startDate and endDate

public class DateRange { private Date start; private Date end; ... public DateRange withStart(Date start) {

this.start = start; return this;

} public DateRange withEnd(Date end) {

this.end = end; return this;

} }

• Use Essence pattern composed of: • Builder • Fluent interface • Validation of required fields before object construction

Page 22: Code and Design Smells. Are They a Real Threat?

Telescoping constructors – fluent builder public class RentalContractBuilder { ... public RentalContractBuilder withCustomer(Customer customer) { this.customer = customer; return this; } public RentalContractBuilder addCar(Car car) { cars.add(car); return this; } public RentalContractBuilder withCars(List<Car> cars) { this.cars = cars; return this; } public RentalContractBuilder withDateRange(DateRange dateRange) { this.dateRange = dateRange; return this; } public void validate() { customerValidator.validate(customer); carValidator.validate(cars); datesValidator.validate(dateRange); } public RentalContract build() { validate(); return new RentalContract(this); } }

Page 23: Code and Design Smells. Are They a Real Threat?

Telescoping constructors – end result

public class RentalContract { ... RentalContract(RentalContractBuilder contractBuilder) { this.customer = contractBuilder.getCustomer(); this.cars = contractBuilder.getCars(); this.dateRange = contractBuilder.getDateRange(); } }

RentalContract rentalContract = new RentalContractBuilder() .withCustomer(customer) .addCar(mustang) .addCar(camaro) .withDateRange(new DateRange().withStart(new Date())) .build();

• Constructor with default access

• We are sure that rentalContract is a valid instance after build()

Page 24: Code and Design Smells. Are They a Real Threat?

Big Ball of Mud – The Mikado Method

• Allows for systematic reshaping of the system

• Easy to use

• Provides stability to the codebase while changing it

• Fits in an incremental process

• Scalable, the whole team can participate

Page 25: Code and Design Smells. Are They a Real Threat?

The Mikado Method

• Set a goal

• Experiment

• Visualize

• Undo

Page 26: Code and Design Smells. Are They a Real Threat?

The Mikado Method - flow

Draw goal

Implement goal naively

Errors?

Come up with immediate solutions and draw them as

sub-goals

Revert changes

Select next goal

Yes

No Change makes sense?

Check in !

All goals completed?

Done!

Yes

Yes

Yes

No

No

Page 27: Code and Design Smells. Are They a Real Threat?

Mikado graph

Mikado Goal

Prerequisite: immediate

solution Another

prerequisite

More prerequisites ..linked to

previous

Can have common

dependencies

Page 28: Code and Design Smells. Are They a Real Threat?

Summary

• Forgive the predecessors

• Take small steps

• Work as a team

• Convince management

• Willingness to change