Designing and Maintaining Software (DAMS) - GitHub
Recommend Documents
Habitable Software. Leaner. Less Complex. Loosely Coupled. More Cohesive. Avoids Duplication. Clearer. More Extensible ?
When we are testing the way that a unit behaves when a condition is met, use a stub to setup the condition. Solution: us
Size != Complexity. âImagine a small (50 line) program comprising. 25 consecutive "IF THEN" constructs. Such a program
Page 1. Getting Lean. Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Lean software⦠Has no extra parts
Automatically detect similar fragments of code. class StuffedCrust def title. "Stuffed Crust " +. @toppings.title +. " P
Open-source. Influenced by Perl, Smalltalk, Eiffel, Ada and Lisp. Dynamic. Purely object-oriented. Some elements of func
ASTs are tree data structures that can be analysed for meaning (following JLJ in SYAC 2014/15) ... More Cohesive. Avoids
Ruby Testing Frameworks. 3 popular options are: RSpec, Minitest and Test::Unit. We'll use RSpec, as it has the most comp
Fixes issue #42. Users were being redirected to the home page after login, which is less useful than redirecting to the
âWe want the reading of code to be easy, even it makes the writing harder. (Of course, there's no way to write code wi
Getting loose coupling. Designing and Maintaining Software (DAMS). Louis Rose ... should not depend on low-level modules
Plug-ins. Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Problem. Page 3. Current Architecture. Shareabl
Tools: Vagrant. Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Bugs that appear in production and that c
Observers. Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Page 3. Delivery people need to know when pizz
Reducing duplication. Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Tactics. Accentuate similarities to
%w.rack tilt date INT TERM..map{|l|trap(l){$r.stop}rescue require l};. $u=Date;$z=($u.new.year + 145).abs;puts "== Almos
What is it? Several pieces of data are often used together. Why is it problematic? Behaviour that operates on the clump
R&D: sketch habitable solutions on paper, using UML. 4. Evaluate solutions and implement the best, using TDD. Probab
Clear Names. Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Naming is hard. âThere are only two hard t
Why not duplicate? Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Habitable Software. Leaner. Less Compl
Clear Documentation. Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Bad documentation. Misleading or con
âWe have tried to demonstrate that it is almost always incorrect to begin the decomposition of a system into modules o
Coupling Between Objects. Counts the number of other classes to which a class is coupled (other than via inheritance). C
providers are examples of issues that may need attention in online settings. .... World Congress in Computer Science, Computer Engineering, & Applied Com- ...... The work resulted among many contributions in a framework and notation for.
Designing and Maintaining Software (DAMS) - GitHub
Getting Cohesion. Designing and Maintaining Software (DAMS). Louis Rose. Page 2. Single Responsibility. Principle. A cla
Getting Cohesion
Designing and Maintaining Software (DAMS) Louis Rose
Single Responsibility Principle
A class should have only one reason to change. - Martin and Martin Chapter 8, Agile Principles, Patterns and Practices in C#, Prentice Hall, 2009
Finding responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
def like! @likes += 1 end
Based on: https://www.youtube.com/watch?v=5yX6ADjyqyE
Finding responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
Finding responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
Finding responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
Finding responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
Finding responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
Finding responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
Tactics Extract objects from primitives and data clumps Move methods to avoid feature envy Extract wrappers for ancillary responsibilities
Primitive Obsession class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
Primitive Obsession class Pizza def like! @likes += 1 end def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end
end
Extract Primitive class Pizza def like! @likes += 1 end def rating Rating.from_likes(likes) end def worse_rating if rating.letter == "E" nil else rating.letter.succ end end end
class Rating def self.from_likes(likes) if likes > 1000 then new("A") elsif likes > 500 then new("B") elsif likes > 250 then new("C") elsif likes > 100 then new("D") else new("E") end end attr_reader :letter def initialize(letter) @letter = letter end end
Extract Primitive class Pizza def like! @likes += 1 end def rating Rating.from_likes(likes) end def worse_rating if rating.letter == "E" nil else rating.letter.succ end end end
class Rating def self.from_likes(likes) if likes > 1000 then new("A") elsif likes > 500 then new("B") elsif likes > 250 then new("C") elsif likes > 100 then new("D") else new("E") end end attr_reader :letter def initialize(letter) @letter = letter end end
Feature Envy class Pizza def like! @likes += 1 end def rating Rating.from_likes(likes) end def worse_rating if rating.letter == "E" nil else rating.letter.succ end end end
class Rating def self.from_likes(likes) if likes > 1000 then new("A") elsif likes > 500 then new("B") elsif likes > 250 then new("C") elsif likes > 100 then new("D") else new("E") end end attr_reader :letter def initialize(letter) @letter = letter end end
Move Method class Pizza def like! @likes += 1 end def rating Rating.from_likes(likes) end def worse_rating if rating.letter == "E" nil else rating.letter.succ end end end
class Rating def self.from_likes(likes) if likes > 1000 then new("A") elsif likes > 500 then new("B") elsif likes > 250 then new("C") elsif likes > 100 then new("D") else new("E") end end attr_reader :letter def initialize(letter) @letter = letter end def worse_rating if letter == "E" nil else
Move Method class Pizza def like! @likes += 1 end def rating Rating.from_likes(likes) end def worse_rating rating.worse_rating end end
class Rating def self.from_likes(likes) if likes > 1000 then new("A") elsif likes > 500 then new("B") elsif likes > 250 then new("C") elsif likes > 100 then new("D") else new("E") end end attr_reader :letter def initialize(letter) @letter = letter end def worse_rating if letter == "E" nil else
Move Method class Pizza def like! @likes += 1 end def rating Rating.from_likes(likes) end def worse_rating rating.worse_rating end end
class Rating def self.from_likes(likes) if likes > 1000 then new("A") elsif likes > 500 then new("B") elsif likes > 250 then new("C") elsif likes > 100 then new("D") else new("E") end end def initialize(letter) @letter = letter end def worse_rating if @letter == "E" nil else @letter.succ end
Move Method class Pizza def like! @likes += 1 end def rating Rating.from_likes(likes) end def worse_rating rating.worse end end
class Rating def self.from_likes(likes) if likes > 1000 then new("A") elsif likes > 500 then new("B") elsif likes > 250 then new("C") elsif likes > 100 then new("D") else new("E") end end def initialize(letter) @letter = letter end def worse if @letter == "E" nil else @letter.succ end
Move Method class Pizza def like! @likes += 1 end def rating Rating.from_likes(likes) end end
class Rating def self.from_likes(likes) if likes > 1000 then new("A") elsif likes > 500 then new("B") elsif likes > 250 then new("C") elsif likes > 100 then new("D") else new("E") end end def initialize(letter) @letter = letter end def worse if @letter == "E" nil else @letter.succ end
Back to responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating if likes > 1000 then "A" elsif likes > 500 then "B" elsif likes > 250 then "C" elsif likes > 100 then "D" else "E" end end def worse_rating if rating == "E" nil else rating.succ end end end
Back to responsibilities class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating Rating.from_likes(likes) end end
Extract ancillaries… class Pizza attr_reader :toppings attr_reader :likes def initialize(toppings) @toppings = toppings @likes = 0 end def title toppings.title end def cost toppings.cost + 4 end def like! @likes += 1 end
def rating Rating.from_likes(likes) end end
… using delegates class Pizza attr_reader :toppings def initialize(toppings) @toppings = toppings end def title toppings.title end def cost toppings.cost + 4 end end
class Likeable < SimpleDelegator attr_reader :likes def initialize(subject) @likes = 0 super end def like! @likes += 1 end def rating Rating.from_likes(likes) end end
… using delegates class Pizza attr_reader :toppings def initialize(toppings) @toppings = toppings end def title toppings.title end def cost toppings.cost + 4 end end
class Likeable < SimpleDelegator attr_reader :likes def initialize(subject) @likes = 0 super end def like! @likes += 1 end def rating Rating.from_likes(likes) end end
Cohesion & Coupling Before the refactoring: Client 1
Client 2
Pizza
Topping
Cohesion & Coupling After the refactoring: Client 1
Pizza
Topping
Client 2
Likeable
Rating
Cohesion & Coupling All clients were coupled to all of Pizza Client 1
Pizza Likeable
Client 2
Rating
Topping
Cohesion & Coupling Clients that don’t need Likeable aren’t affected by it Client 1
Pizza
Topping
Client 2
Likeable
Rating
Cohesion & Coupling Likeable can be re-used in new contexts Client 1
Pizza
Topping
Client 2
Likeable
Rating
Client 3
Summary Classes should have a single responsibility: a single reason to change Classes with low cohesion normally have more than one responsibility Extract classes to redistribute responsibilities