LAB OF SOFTWARE ARCHITECTURES AND INFORMATION SYSTEMS FACULTY OF INFORMATICS MASARYK UNIVERSITY, BRNO AND RELATED PRINCIPLES Barbora Bühnová buhnova@fi.muni.cz CLEAN CODE, CODE SMELLS, REFACTORING Outline of the lecture • Motivation • The role of naming •  Classes, methods, variables • The role of code structure •  Duplication •  Cohesion and coupling •  S.O.L.I.D. principles •  DRY and GRASP principles • Bad code smells • Refactoring •  When, how, where © B. Bühnová, PV260 Software Quality Why are we here? ! DO YOU KNOW HOW EXPENSIVE IT IS TO ! HAVE YOU GUYS AROUND? © B. Bühnová & V. Dusch [1] How is that related to code quality? Your codebase is just like the database of a website. • Read:Write ratio is like 10:1 … AND BRAINS SUCK AS CACHES! Goal is cheap Reads • People will read your code again and again and again and ... • How long does it really take to understand your module? © B. Bühnová & V. Dusch [1] Why is our code hard to understand? • Management? • Unclear requirements? • Customers? • Schedules? • Requirement changes? NO. BECAUSE WE WROTE IT LIKE THAT! SO HOW TO GET IT RIGHT? © B. Bühnová & V. Dusch [1] Two rules of understandable code 1.  Name things right • Reveal intent, self-documented code. What about comments? 2.  Balance code structure • No God classes, long methods and other bad code smells. IT IS EASY TO WRITE CODE THAT A MACHINE UNDERSTANDS. WRITING CODE THAT ANOTHER HUMAN CAN UNDERSTAND IS A LOT HARDER. © B. Bühnová & V. Dusch [1] Let’s name things! CLASSES, METHODS, VARIABLES, … © B. Bühnová & V. Dusch [1] Classes • Name it after its purpose – Single responsibility principle! THE CLASS NAME IS THE SINGLE MOST IMPORTANT ! DEFINITION OF WHAT FITS INTO THAT CLASS.! • Using generic names throws that away! ApplicationManager, FrameworkDataInformationProvider, UtilityDataProcessor © B. Bühnová & V. Dusch [1] Proper class names lead to smaller classes • Should we let our Email class figure out attachment mime types? • By always asking if stuff fits we can ‘discover’ new classes in our applications • EmailAttachment, ImageEmailAttachment? © B. Bühnová & V. Dusch [1] And we care because? • Big classes rob you of all OO benefits • You depend on way to much other stuff • Usually tightly coupled • You can extend from those classes and chances are you can’t swap out either • You don’t get nice interfaces as well © B. Bühnová & V. Dusch [1] Methods • Does it return a boolean? • Call it hasX or isX • Hidden booleans status = user.getStatus(); if(status == user.STATUS_BANNED) { } if(user.isBanned()) { } © B. Bühnová & V. Dusch [1] Getters and Setters • Getter retrieve internal state • Setters modify internal state • Both should not modify anything else • Don’t make your setters into liars • Make sure that setters only return null! © B. Bühnová & V. Dusch [1] No boolean parameters • If you don’t have a very good reason! user.setAdminStatus(false); user.setAdminStatus(true); • vs. user.revokeAdminRights(); user.grantAdminRights(); © B. Bühnová & V. Dusch [1] Classes are nouns, methods start with verbs! X.createStuff(); X.deleteStuff() X.dispatchCall(); X.subscribeUser(); • But never user.admin(); or user.bag(); list.subscription(); © B. Bühnová & V. Dusch [1] Agree on verbs for actions • Can you tell me the difference between directory.delete(entry); and directory.remove(entry); router.dispatch/delegate(call); list.add/append(user); • Different actions need distinguishable names! Even if that makes function names longer. © B. Bühnová & V. Dusch [1] Always favor long function names? NO! • A short an precise public API is important • Precise names help a lot to create readable client code. • But you want long privates • Internally used functions can be named as verbosely as needed to communicate their intent and behavior. © B. Bühnová & V. Dusch [1] Readable classes Lots of small descriptive methods, best arranged as: 1.  First you build it 2.  Then you call it 3.  Then it does work 4.  Most work happens in private methods class Log { public Log(SplFileInfo fileInfo) {} public void log() {} private bool isLogfileWriteable() {} private void createLogfileIfNecaccary() {} private void writeToLogfile() {} private void flushCurrentLogfileBuffer() {} } © B. Bühnová & V. Dusch [1] Last thing to name are Variables Rules of thumb: • Descriptive function parameters • Big scope: long name • Short scope: short name © B. Bühnová & V. Dusch [1] Wrapping up naming • "A name, like an honorable human, should say what it means and mean what it says” • Names are the basis of our communication! • They can speed up things immensely! © B. Bühnová & V. Dusch [1] And what about comments? • I’ve spend a lot of time ranting about comments. Name things properly! • Additional Resources: http://edorian.posterous.com/they-told-you-to-document- everything-they-lie http://www.slideshare.net/Edorian/php-unconference- europa-clean-code-stop-wasting-my-time © B. Bühnová & V. Dusch [1] Enough of naming? LET’S MOVE ON TO CODE STRUCTURE! © B. Bühnová & V. Dusch [1] No duplication • What can be duplicated? •  Code Blocks •  Methods •  Classes •  Functions •  Components •  Exceptions • Minimalistic code • What minimalism mean? What do we want to minimize? What can be minimized? © B. Bühnová & O. Krajíček [2] High cohesion and low coupling Cohesion... • How strongly-related and focused are the various responsibilities of a module. • Classes with low cohesion have a split personality. • Coupling... • The degree to which each program module relies on each one of the other modules. • Coupling is directly related to decomposition and you need to keep it in mind when you decompose. © B. Bühnová & O. Krajíček [2] S.O.L.I.D. principles • Single Responsibility Principle • Open / Closed Principle • Liskov Substitution Principle • Interface Seggregation Principle • Dependency Inversion © B. Bühnová, PV260 Software Quality SRP: The Single Responsibility Principle THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE. ! Robert C. “Uncle Bob” Martin • Why a reason for a class to change? Why not responsibility? Because this is all interconnected! •  The more responsibilities, the more dependencies. •  The more dependencies, the higher risk of change propagation. •  The bigger change propagation, the higher risk of error. • Following SRP leads to lower coupling and higher cohesion. • Many small classes with distinct responsibilities result in a more flexible design. © B. Bühnová & A. Masood [3] SRP: The Single Responsibility Principle •  public abstract class BankAccount { double Balance { get; } void Deposit(double amount); void Withdraw(double amount); void Transfer(double amount, IBankAccount toAccount); void AddInterest(double amount); } Refactor to: public abstract class BankAccount { double Balance { get; } void Deposit(double amount); void Withdraw(double amount); void Transfer(double amount, IBankAccount toAccount); } public class CheckingAccount : BankAccount { } public class SavingsAccount : BankAccount { public void AddInterest(double amount); } © B. Bühnová & Los Techies [4] SRP Summary • Following SRP leads to lower coupling and higher cohesion. • Many small classes with distinct responsibilities result in a more flexible design. © B. Bühnová & A. Masood [3] OCP: The Open/Closed Principle Open to Extension New behavior can be added in the future Closed to Modification Changes to source or binary code are not required • Bertrand Meyer originated the OCP term in his 1988 book, Object Oriented Software Construction. In OOP, abstractions include: • Interfaces • Abstract classes © B. Bühnová & A. Masood [3] Why is that a problem? • We want to avoid introducing changes that cascade through many modules in our application • Writing new classes is less likely to introduce problems! •  Nothing depends on new classes (yet). •  New classes have no legacy coupling to make them hard to design or test. • Remember TANSTAAFL •  There Ain’t No Such Thing As A Free Lunch •  OCP adds complexity to design! •  Don’t apply OCP at first If the module changes once, accept it. If it changes a second time, refactor to achieve OCP © B. Bühnová & A. Masood [3] OCP Summary • Conformance to OCP yields flexibility, reusability, and maintainability. • Know which changes to guard against, and resist premature abstraction. © B. Bühnová & A. Masood [3] LSP: The Liskov Substitution Principle THE LISKOV SUBSTITUTION PRINCIPLE STATES THAT SUBTYPES MUST BE SUBSTITUTES FOR THEIR BASE TYPES. ! Agile Principles, Patterns, and Practices in C# •  Named for Brabara Liskov, who first described the principle in 1988. Substitutability: •  Child classes must not: •  Remove base class behavior •  Violate base class invariants •  And in general must not require calling code to know they are different from their base type. •  To follow LSP, derived classes must not violate any constraints defined (or assumed by clients) on the base classes. © B. Bühnová & A. Masood [3] The Problem •  Non-substitutable code breaks polymorphism •  Client code expects child classes to work in place of their base classes •  “Fixing” substitutability problems by adding if-then or switch statements quickly becomes a maintenance nightmare (and violates OCP) LSP violation: foreach (var emp in Employees) { if( emp is Manager ) { _printer.PrintManager( emp as Manager ); } else { _printer.PrintEmployee( emp ); } } © B. Bühnová & A. Masood [3] LSP Summary • Conformance to LSP allows for proper use of polymorphism and produces more maintainable code. • Remember IS-SUBSTITUTABLE-FOR instead of IS-A. Consider Refactoring to a new Base Class • Given two classes that share a lot of behavior but are not substitutable. • Create a third class that both can derive from. • Ensure substitutability is retained between each class and the new base. © B. Bühnová & A. Masood [3] ISP: The Interface Segregation Principle THE ISP STATES THAT CLIENTS SHOULD NOT BE FORCED TO DEPEND ON METHODS THEY DO NOT USE. ! Agile Principles, Patterns, and Practices in C# • That is, prefer small, cohesive interfaces to “fat” interfaces. • What is an interface? Interface keyword/type public interface IDoSomething { … } Public interface of a class public class SomeClass { … } © B. Bühnová & A. Masood [3] The Problem • Client references a class but only uses small portion of it • Interface Segregation violations result in classes that depend on things they do not need, increasing coupling and reducing flexibility and maintainability • Unimplemented interface methods © B. Bühnová & A. Masood [3] When do we fix ISP? • Once there is pain o If there is no pain, there’s no problem to address. • If you find yourself depending on a “fat” interface you own o Create a smaller interface with just what you need o Have the fat interface implement your new interface o Reference the new interface with your code • If you find “fat” interfaces are problematic but you do not own them o Create a smaller interface with just what you need o Implement this interface using an Adapter that implements the full interface © B. Bühnová & A. Masood [3] ISP Summary • Don’t force client code to depend on things it doesn’t need. • Keep interfaces small, cohesive, and focused • Whenever possible, let the client define the interface • Whenever possible, package the interface with the client o Alternately, package in a third assembly client and implementation both depend upon o Last resort: Package interfaces with their implementation © B. Bühnová & A. Masood [3] DIP: The Dependency Inversion Principle HIGH-LEVEL MODULES SHOULD NOT DEPEND ON LOWLEVEL MODULES. BOTH SHOULD DEPEND ON ABSTRACTIONS. © B. Bühnová & A. Masood [3] Traditional Programming and Dependencies • High Level modules call Low Level modules • User Interface depends on o Business Logic depends on o Infrastructure o Utility o Data Access • Static methods are used for convenience or as Façade layers • Class instantiation / Call stack logic is scattered through all modules o Violation of Single Responsibility Principle © B. Bühnová & A. Masood [3] The Problem • Dependencies Flow Toward Infrastructure • Core / Business / Domain Classes Depend on Implementation Details • Result o Tight coupling o No way to change implementation details without recompile (OCP violation) o Difficult to test © B. Bühnová & A. Masood [3] Classes should declare what they need •  Class constructor should require any dependencies the class needs. •  Classes whose constructors make this clear have explicit dependencies. Classes that do not, have implicit, hidden dependencies. public class HelloWorldExplicit { private readonly DateTime _timeOfGreeting; public HelloWorldExplicit(DateTime timeOfGreeting) { _timeOfGreeting = timeOfGreeting; } public string Hello(string name) { if (_timeOfGreeting.Hour < 12) return "Good morning, " + name; if (_timeOfGreeting.Hour < 18) return "Good afternoon, " + name; return "Good evening, " + name; } } © B. Bühnová & A. Masood [3] DIP: Summary • Depend on abstractions. • Don’t force high-level modules to depend on low-level modules through direct instantiation or static method calls. • Declare class dependencies explicitly in their constructors. Dependency injection • Inject dependencies via constructor, property, or parameter injection. © B. Bühnová & A. Masood [3] DRY: Don’t Repeat Yourself “EVERY PIECE OF KNOWLEDGE MUST HAVE A SINGLE, UNAMBIGUOUS REPRESENTATION IN THE SYSTEM.”! The Pragmatic Programmer Variations include: • Once and Only Once • Duplication Is Evil (DIE) © B. Bühnová & A. Masood [3] Analysis • Magic Strings/Values • Duplicate logic in multiple locations • Repeated if-then logic • Conditionals instead of polymorphism • Repeated Execution Patterns • Lots of duplicate, probably copy-pasted, code © B. Bühnová & A. Masood [3] DRY: Summary • Repetition breeds errors and waste • Refactor code to remove repetition Repetition in process • Testing o Performing testing by hand is tedious and wasteful • Builds o Performing builds by hand is tedious and wasteful • Deployments o Performing deployments by hand is tedious and wasteful © B. Bühnová & A. Masood [3] GRASP • General Responsibility Assignment Software Patterns • A set of design patterns or aspects emphasizing good coding practices. • Might be useful, but only after you have good understanding of SOLID principles. There is quite a lot to it. Google it if you are interested! © B. Bühnová & O. Krajíček [2] Ready for more? ! BUT WHAT IF WE ALREADY HAVE THE CODE? HOW CAN WE FIND OUT IT SMELLS AND HOW CAN REFACTORING HELP US WITH THAT? © B. Bühnová, PV260 Software Quality Bad Code Smells • Different abstraction levels (not top down - mixed, skipping levels, mixing levels in one method) • Low cohesion (God classes, long methods, script/program wrapped as a class) • Circular dependencies (between classes - mother of all tight couplings) • Duplicated code • Long parameter list • … and many many more © B. Bühnová & M. Osovský [5] What is refactoring? • Refactoring is the process of changing a so:ware system in such a way that it does not alter the external behavior of the code yet improves its internal structure • Refactoring (noun): a change made to the internal structure of so:ware to make it easier to understand and cheaper to modify without changing its observable behavior. • Refactor (verb): to restructure so6ware by applying a series of refactorings without changing its observable behavior. © B. Bühnová & I. Crvenov [6] When to refactor? When you have the refactoring hat on your head! • As part of the routine (e.g. TDD). • After you find weak code (boy scout rule), or need to fix a bug. • Before and/or after you introduce code of a new feature (or a new technology like IoC container). • Long term planned refactoring Only when it leads to faster delivery and better maintenance. Clean code is a means to this end. © B. Bühnová & M. Osovský [5] Where to refactor? Where to refactor? © B. Bühnová & M. Osovský [5] How to refactor? • Use IDE all the time (even when renaming!). Run tests before and after! • Boundary tests (testing published interfaces) should stay green. • Know most common refactorings (extract ..., rename, move, introduce) - learn to use them as part of your IDE mastery. © B. Bühnová & M. Osovský [5] And how MarEn Fowler does it? • M.F: “Whenever I do refactoring, the first step is always the same. I need to build a solid set of tests for that secEon of code. The tests are essenEal because even though I follow refactorings structured to avoid most of the opportuniEes for introducing bugs, I'm sEll human and sEll make mistakes. Thus I need solid tests.” Tip: Before you start refactoring, check that you have a solid suite of tests. These tests must be self-checking © B. Bühnová & I. Crvenov [6] Refactoring methods Extract Method • You have a code fragment that can be grouped together. Turn the fragment in to a method whose name explains the purpose of the method. • Extract Method is one of the most common refactorings being done. One looks at a method that is too long or look at code that needs a comment to understand its purpose. One then turns that fragment of code into its own method. © B. Bühnová & I. Crvenov [6] Extract method Before refactoring A:er Refactoring void printOwing(double amount) { printBanner(); printDetails(amount); } void printDetails(double amount) { WriteLine("name:" + _name); WriteLine("amount" + amount); } void printOwing(double amount) { printBanner(); //print details WriteLine("name:" + _name); WriteLine("amount" + amount); } © B. Bühnová & I. Crvenov [6] Inline method Before Refactoring A:er Refactoring int getRating() { return (moreThanFiveLateDeliveries()) ? 2 : 1; } boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; } int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1; } © B. Bühnová & I. Crvenov [6] Maximum method length? • Maximum method length? 6 Lines ought to be enough for everybody? Smaller is harder to write • Writing ONLY small functions is a SKILL • It is easy to write big functions! • But the first change makes it all worth! © B. Bühnová & V. Dusch [1] Split temporary variable •  You have a temporary variable assigned to more than once, but is not a loop variable nor a collecEng temporary variable. Make a separate temporary variable for each assignment. Before Refactoring double temp = 2 * (_height + _width); WriteLine(temp); temp = _height * _width; WriteLine(temp); A:er Refactoring double perimeter = 2 * (_height + _width); WriteLine(perimeter); double area = _height * _width; WriteLine(area); © B. Bühnová & I. Crvenov [6] Remove assignments to parameters • The code assigns to a parameter. Use a temporary variable instead. Before Refactoring int discount (int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2; ... } A:er refactoring int discount (int inputVal, int quantity, int yearToDate) { int result = inputVal; if (inputVal > 50) result -= 2; ... } © B. Bühnová & I. Crvenov [6] Replace method with method object •  You have a long method that uses local variables in such a way that you cannot apply Extract Method. Turn the method into its own object so that all the local variables become fields on that object. You can then decompose the method into other methods on the same object. © B. Bühnová & I. Crvenov [6] Move method • A method is, or will be, using or used by more features of another class than the class on which it is defined. Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegaEon, or remove it altogether. © B. Bühnová & I. Crvenov [6] Move field • A field is, or will be, used by another class more than the class on which it is defined. Create a new field in the target class, and change all its users. © B. Bühnová & I. Crvenov [6] Extract class • You have one class doing work that should be done by two. Create a new class and move the relevant fields and methods from the old class into the new class. © B. Bühnová & I. Crvenov [6] Inline class • A class isn't doing very much.Move all its features into another class and delete it. • Inline Class is the reverse of Extract Class. I use Inline Class if a class is no longer pulling its weight and shouldn't be around any more. O:en this is the result of refactoring that moves other responsibiliEes out of the class so there is liWle le:. © B. Bühnová & I. Crvenov [6] Hide delegate • A client is calling a delegate class of an object. Create methods on the server to hide the delegate. Does it make sense to reverse this refactoring? © B. Bühnová & I. Crvenov [6] Remove the middle man • A class is doing too much simple delegaEon. Get the client to call the delegate directly. © B. Bühnová & I. Crvenov [6] Replace data with value object • You have a data item that needs addiEonal data or behavior. Turn the data item into an object. © B. Bühnová & I. Crvenov [6] Replace array with object —  You have an array in which certain elements mean different things. Replace the array with an object that has a field for each element. String[] row = new String[3]; row [0] = "Liverpool”; row [1] = "15"; After Refactoring: Performance row = new Performance(); row.setName("Liverpool"); row.setWins("15"); © B. Bühnová & I. Crvenov [6] Replace magic number with a constant —  You have a literal number with a particular meaning. Create a constant, name it after the meaning, and replace the number with it. double potentialEnergy(double mass, double height) { return mass * 9.81 * height; } double potentialEnergy(double mass, double height) { return mass * GRAVITATIONAL_CONSTANT * height; } static final double GRAVITATIONAL_CONSTANT = 9.81; © B. Bühnová & I. Crvenov [6] Encapsulate field • There is a public field. Make it private and provide accessors. public String _name; After Refactoring: private String _name; public String getName() {return _name;} public void setName(String arg) {_name = arg;} © B. Bühnová & I. Crvenov [6] Encapsulate collecEon —  A method returns a collection. Make it return a read-only view and provide add/remove methods. Motivation Often a class contains a collection of instances. This collection might be an array, list, set, or vector. Such cases often have the usual getter and setter for the collection. © B. Bühnová & I. Crvenov [6] Decompose condiEonal —  You have a complicated conditional (if-then-else) statement. Extract methods from the condition, then part, and else parts. if (date.before (SUMMER_START) || date.after(SUMMER_END)) charge = quantity * _winterRate + _winterServiceCharge; else charge = quantity * _summerRate; After Refactoring: if (notSummer(date)) charge = winterCharge(quantity); else charge = summerCharge (quantity); © B. Bühnová & I. Crvenov [6] Consolidate conditional expression —  You have a sequence of conditional tests with the same result. Combine them into a single conditional expression and extract it. double disabilityAmount() { if (_seniority < 2) return 0; if (_monthsDisabled > 12) return 0; if (_isPartTime) return 0; .. After Refactoring: // compute the disability amount double disabilityAmount() { if (isNotEligableForDisability()) return 0; .. © B. Bühnová & I. Crvenov [6] Consolidate duplicate conditional fragments • The same fragment of code is in all branches of a condiEonal expression. Move it outside of the expression if (isSpecialDeal()) { total = price * 0.95; send(); } else { total = price * 0.98; send(); } A:er Refactoring: if (isSpecialDeal()) total = price * 0.95; else total = price * 0.98; send(); © B. Bühnová & I. Crvenov [6] Replace nested conditional with guard classes •  A method has condiEonal behavior that does not make clear the normal path of execuEon. Use guard clauses for all the special cases double getPayAmount() { double result; if (_isDead) result = deadAmount(); else { if (_isSeparated) result = separatedAmount(); else { if (_isRetired) result = retiredAmount(); else result = normalPayAmount(); }; } return result; }; After Refactoring: double getPayAmount() { if (_isDead) return deadAmount(); if (_isSeparated) return separatedAmount(); if (_isRetired) return retiredAmount(); return normalPayAmount(); }; © B. Bühnová & I. Crvenov [6] Rename method • The name of a method does not reveal its purpose. Change the name of the method © B. Bühnová & I. Crvenov [6] Separate query from modifier • You have a method that returns a value but also changes the state of an object. Create two methods, one for the query and one for the modificaCon. © B. Bühnová & I. Crvenov [6] Parameterized method • Several methods do similar things but with different values contained in the method body. Create one method that uses a parameter for the different values. © B. Bühnová & I. Crvenov [6] Introduce parameter to object • You have a group of parameters that naturally go together. Replace them with an object. © B. Bühnová & I. Crvenov [6] Remove se^ng methods • A field should be set at creaEon Eme and never altered. Remove any seFng method for that field. • Providing a se^ng method indicates that a field may be changed. If you don't want that field to change once the object is created, then don't provide a se^ng method (and make the field final). That way your intenEon is clear and you o:en remove the very possibility that the field will change. © B. Bühnová & I. Crvenov [6] Hide method • A method is not used by any other class. Make the method private © B. Bühnová & I. Crvenov [6] Pull up field • Two subclasses have the same field. Move the field to the superclass. © B. Bühnová & I. Crvenov [6] Pull up method • Two subclasses have the same field. Move the field to the superclass. © B. Bühnová & I. Crvenov [6] Push down method • Behavior on a superclass is relevant only for some of its subclasses. Move it to those subclasses © B. Bühnová & I. Crvenov [6] Extract subclass • A class has features that are used only in some instances. Create a subclass for that subset of features © B. Bühnová & I. Crvenov [6] Extract superclass • You have two classes with similar features. Create a superclass and move the common features to the superclass. © B. Bühnová & I. Crvenov [6] Collapse hierarchy • A superclass and subclass are not very different. Merge them together. © B. Bühnová & I. Crvenov [6] Refactoring and performance • To make the so:ware easier to understand, you o:en make changes that will cause the program to run more slowly. • However, the calling overhead is very small and pays off! © B. Bühnová & I. Crvenov [6] Takeaways • It always pays off to make your code easier to understand. • Write self-documenting code, use naming (not comments) to express code’s intent. • Understand S.O.L.I.D. principles and do not be afraid to restructure your code into MANY small classes and methods. • Refactoring and testing make you FASTER! •  Bad code smells will navigate you. Barbora Bühnová, FI MU Brno buhnova@fi.muni.cz www.fi.muni.cz/~buhnova contact me thanks for listening © B. Bühnová, PV260 Software Quality References •  [1] V. Dusch: Stop wasting time through clean code •  [2] Ondřej Krajíček: PV260 lecture in Spring 2015 •  [3] Adnan Masood: Refactoring Code to a Solid Foundation •  [4] Los Techies: Pablo's SOLID Software Development •  [5] Martin Osovský: PV260 lecture in Spring 2015 •  [6] Igor Crvenov: Refactoring Tips by Marin Fowler © B. Bühnová, PV260 Software Quality