Se ha denunciado esta presentación.
Se está descargando tu SlideShare. ×
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Próximo SlideShare
SOLID mit Java 8
SOLID mit Java 8
Cargando en…3
×

Eche un vistazo a continuación

1 de 53 Anuncio

SOLID Java Code

Descargar para leer sin conexión

Talk given at the London Java Community's meetup on the application of SOLID coding principles using the Java programming language.

Talk given at the London Java Community's meetup on the application of SOLID coding principles using the Java programming language.

Anuncio
Anuncio

Más Contenido Relacionado

Presentaciones para usted (20)

Similares a SOLID Java Code (20)

Anuncio

Más reciente (20)

Anuncio

SOLID Java Code

  1. 1. SOLID Java Code Omar Bashir https://uk.linkedin.com/in/obprofile @OmarBashir_40 obashir@yahoo.com
  2. 2. Coding Humanely ● What if our code had feelings ? ● What if we treated people like we treat code ? – e.g., Sir, you will need a foot transplant to be able to use our latest range of trainers.
  3. 3. Desirable Code Qualities ● Simplicity – Ease of understanding. ● Extensibility – Ease of extending functionality. ● Flexibility – Ease of adapting to the operating environment. ● Testability – Ease of testing from units of code to the entire systems
  4. 4. Challenges: Size and Complexity ● Problems – Most real world problems requiring automation are complex. ● Requirements – Increasing demand for comprehensive automation. ● Solutions – Resulting solutions are large and sophisticated.
  5. 5. Modularisation ● Decomposing large systems into smaller and simpler parts. – Functions and procedures. – Classes and modules. ● Building the parts. ● Assembling the parts to complete the system. ● To achieve desirable code qualities parts should be interchangeable.
  6. 6. Keys to Interchangeability ● Coupling – Interdependence between different software modules. – Low coupling is preferred as it allows flexibility and extensibility. – Low coupling via simple and stable interfaces. ● Cohesion – Degree to which elements of a module functionally belong together. – Higher cohesion reduces complexity of components. ● Higher cohesion and low coupling enables interchangeability.
  7. 7. Spot Issues ? package app; import calc.Calculator; public class App { public static void main(String[] args) { String line = System.console().readLine(); String[] parts = line.split(" "); Calculator.operand1 = Double.parseDouble(parts[0]); Calculator.operator = parts[1].charAt(0); Calculator.operand2 = Double.parseDouble(parts[2]); Calculator.calculate(); } }
  8. 8. Spot Issues ? package app; import calc.Calculator; public class App { public static void main(String[] args) { String line = System.console().readLine(); String[] parts = line.split(" "); Calculator.operand1 = Double.parseDouble(parts[0]); Calculator.operator = parts[1].charAt(0); Calculator.operand2 = Double.parseDouble(parts[2]); Calculator.calculate(); } } High Coupling
  9. 9. Spot Issues ? package calc; public class Calculator { public static double operand1; public static double operand2; public static char operator; public static void calculate() { double result = Double.NaN; switch(operator) { case '+': result = operand1 + operand2; break; case '-': result = operand1 - operand2; break; case '*': result = operand1 * operand2; break; case '/': result = operand1 / operand2; break; default: System.out.printf("Unrecognised operator %cn", operator); } System.out.printf("%f %c %f = %fn", operand1, operator, operand2, result); } }
  10. 10. Spot Issues package calc; public class Calculator { public static double operand1; public static double operand2; public static char operator; public static void calculate() { double result = Double.NaN; switch(operator) { case '+': result = operand1 + operand2; break; case '-': result = operand1 - operand2; break; case '*': result = operand1 * operand2; break; case '/': result = operand1 / operand2; break; default: System.out.printf("Unrecognised operator %cn", operator); } System.out.printf("%f %c %f = %fn", operand1, operator, operand2, result); } } High Coupling Low Cohesion
  11. 11. Simplifying with SOLID ● Types of coupling – Content coupling, Common coupling, Stamp coupling, Control coupling, Data coupling. ● Types of cohesion – Co-incidental cohesion, Logical cohesion, Temporal cohesion, Procedural cohesion, Communicational cohesion, Sequential cohesion, Functional cohesion. ● SOLID – Five principles of object oriented programming. – Aims to deliver extensible and maintainable software.
  12. 12. SOLID ● Single Responsibility Principle (SRP) – A class should have a single responsibility. ● Open Close Principle (OCP) – Open for extension but closed for modification. ● Liskov Substitution Principle (LSP) – Substitution of objects by instances of sub-classes. ● Interface Segregation Principle (ISP) – Many client specific interfaces instead of one big one. ● Dependency Inversion Principle (DIP) – Depend on abstractions instead of implementations.
  13. 13. 1 Single Responsibility Principle
  14. 14. Single Responsibility Principle ● A class should have one and only one reason to change. – A class should have only one responsibility. ● Promotes high cohesion and low coupling. – High cohesion because of focused classes. – Low coupling because of dependency on other cohesive classes.
  15. 15. Example public class MeanCalculator { public double calculate(final String data) { double sum = 0.0; final String[] parsedData = data.split(","); for (String item : parsedData) { sum += Double.parseDouble(item); } return sum/parsedData.length; } }
  16. 16. Example public class MeanCalculator { public double calculate(final String data) { double sum = 0.0; final String[] parsedData = data.split(","); for (String item : parsedData) { sum += Double.parseDouble(item); } return sum/parsedData.length; } }
  17. 17. Example public class MeanCalculator { public double calculate(final String data) { double sum = 0.0; final String[] parsedData = data.split(","); for (String item : parsedData) { sum += Double.parseDouble(item); } return sum/parsedData.length; } } Two reasons to change 1. Parsing 2. Computation
  18. 18. Example Functional (Java 8) Implementation public class MeanCalculator { public double calculate(final List<Double> data) { double sum = 0.0; for (Double item : data) { sum += item; } return sum/data.size(); } } public class MeanCalculator { public double calculate(final List<Double> data) { return data. stream(). collect(Collectors. averagingDouble(item -> item)); } }
  19. 19. 2 Open Close Principle
  20. 20. Open/Close Principle ● Software entities (Classes, methods, modules etc.) should be open for extension but closed for modification. – Changing functionality need not require modifying existing code. ● Preferred approaches, – Inheritance, – Composition of abstractions (plugins).
  21. 21. Example public class Parser { List<Double> parse(final String data) { final String[] parsedCsv = data.split(","); return toDoubleList(parsedCsv); } private List<Double> toDoubleList(final String[] data) { final List<Double> list = new ArrayList<>(); for(String item: data) { list.add(Double.parseDouble(item)); } return list; } }
  22. 22. Example:Extendinga ClosedParser public class Parser { public List<Double> parse(final String data, final Format format) { String[] parsedData = null; switch(format){ case CSV: parsedData = data.split(","); break; case XML: try { parsedData = parseXml(data); } catch (Exception exp) { throw new IllegalArgumentException(exp); } break; default: throw new IllegalArgumentException(String.format("Unknown format %s", format)); } return toDoubleList(parsedData); } /** * Format: <DataList><Item>3</Item><Item>4.5</Item><Item>7.5</Item></DataList> */ private String[] parseXml(final String xmlString) throws Exception { final Document xmlDoc = DocumentBuilderFactory. newInstance(). newDocumentBuilder(). parse(new ByteArrayInputStream(xmlString.getBytes())); final NodeList nodes = xmlDoc.getElementsByTagName("Item"); final String[] textList = new String[nodes.getLength()]; for (int i = 0; i < nodes.getLength(); i++) { textList[i] = nodes.item(i).getTextContent().trim(); } return textList; } private List<Double> toDoubleList(final String[] data) { final List<Double> list = new ArrayList<>(); for(String item: data) { list.add(Double.parseDouble(item)); } return list; } }
  23. 23. public List<Double> parse(final String data, final Format format) { String[] parsedData = null; switch(format){ case CSV: parsedData = data.split(","); break; case XML: try { parsedData = parseXml(data); } catch (Exception exp) { throw new IllegalArgumentException(exp); } break; default: throw new IllegalArgumentException( String.format("Unknown format %s", format)); }
  24. 24. Example: Opening With Template Method public abstract class Parser { public List<Double> parse(final String data) { final String[] parsedData = split(data); return toDoubleList(parsedData); } protected abstract String[] split(final String data); private List<Double> toDoubleList(final String[] data) { final List<Double> list = new ArrayList<>(); for(String item: data) { list.add(Double.parseDouble(item)); } return list; } }
  25. 25. Example: Parser Using Streams public abstract class Parser { public List<Double> parse(final String data) { final String[] parsedData = split(data); return Arrays.asList(parsedData).stream(). map(item -> Double.parseDouble(item)). collect(Collectors.toList()); } protected abstract String[] split(final String data); }
  26. 26. Example: Opening With Template Method public class CsvParser extends Parser { @Override protected String[] split(String data) { return data.split(","); } }
  27. 27. public class XmlParser extends Parser { /** * Format: * <DataList><Item>3</Item><Item>4.5</Item><Item>7.5</Item></DataList> */ @Override protected String[] split(String data) { String[] textList = null; try { final Document xmlDoc = DocumentBuilderFactory. newInstance(). newDocumentBuilder(). parse(new ByteArrayInputStream(data.getBytes())); final NodeList nodes = xmlDoc.getElementsByTagName("Item"); textList = new String[nodes.getLength()]; for (int i = 0; i < nodes.getLength(); i++) { textList[i] = nodes.item(i).getTextContent().trim(); } } catch (Exception exp) { throw new IllegalArgumentException(exp); } return textList; } }
  28. 28. 3 Liskov Substitution Principle
  29. 29. Liskov Substitution Principle ● Named after MIT professor Barbara Liskov. ● Functions that use references to base classes must be able to use objects of the derived class without knowing it. – Derived classes must be substitutable for base class. ● Caution, – Should not alter the behaviour of the application. – Inheritance hierarchies should not violate domain concepts. ● E.g., a square is a rectangle instead of both are shapes.
  30. 30. public class AvergerApp { public static void main(String[] args) { final List<String> params = Arrays.asList(args); if ((params.size() != 2) || (params.stream().filter(i -> i.contains("=")).count() != 2)) { System.err.println("Parameters: f=<CSV/XML> m=<message>"); } else { final String format = params.stream(). filter(str -> str.contains("f")).findFirst().get().split("=")[1]; final String message = params.stream(). filter(str -> str.contains("m")).findFirst().get().split("=")[1]; final Parser parser = getParser(format); final List<Double> data = parser.parse(message); final double avg = new MeanCalculator().calculate(data); System.out.printf("Average(%s) = %f", data, avg); } } private static Parser getParser(final String format) { Parser parser = null; switch (format) { case "CSV": parser = new CsvParser(); break; case "XML": parser = new XmlParser(); break; default: throw new IllegalArgumentException("Unknown format " + format)); } return parser; } }
  31. 31. public class AvergerApp { public static void main(String[] args) { final List<String> params = Arrays.asList(args); if ((params.size() != 2) || (params.stream().filter(i -> i.contains("=")).count() != 2)) { System.err.println("Parameters: f=<CSV/XML> m=<message>"); } else { final String format = params.stream(). filter(str -> str.contains("f")).findFirst().get().split("=")[1]; final String message = params.stream(). filter(str -> str.contains("m")).findFirst().get().split("=")[1]; final Parser parser = getParser(format); final List<Double> data = parser.parse(message); final double avg = new MeanCalculator().calculate(data); System.out.printf("Average(%s) = %f", data, avg); } } private static Parser getParser(final String format) { Parser parser = null; switch (format) { case "CSV": parser = new CsvParser(); break; case "XML": parser = new XmlParser(); break; default: throw new IllegalArgumentException("Unknown format " + format)); } return parser; } }
  32. 32. 4 Interface Segregation Principle
  33. 33. Interface Segragation Principle ● Multiple fine grained client or domain specific interfaces are better than few coarse grained interfaces. ● Consequences – Classes only implement interfaces that are requirement specific. – Client classes are exposed only to the functionality that they need.
  34. 34. Example public interface AuthorisationService { boolean isAuthorised(String userId, Resource resource, Action action); List<Entitlement> entitlementReport(String userId); List<Group> membershipReport(String userId); boolean entitle(Group group, Entitlement entitlement); boolean entitle(String userId, Entitlement entitlement); boolean add(String userId, Group group); boolean remove(String userId, Group group); boolean remove(String userId, Entitlement entitlement); boolean remove(Group group, Entitlement entitlement); }
  35. 35. Example public interface AuthorisationService { boolean isAuthorised(String userId, Resource resource, Action action); List<Entitlement> entitlementReport(String userId); List<Group> membershipReport(String userId); boolean entitle(Group group, Entitlement entitlement); boolean entitle(String userId, Entitlement entitlement); boolean add(String userId, Group group); boolean remove(String userId, Group group); boolean remove(String userId, Entitlement entitlement); boolean remove(Group group, Entitlement entitlement); }
  36. 36. Example public interface AuthorisationService { boolean isAuthorised(String userId, Resource resource, Action action); List<Entitlement> entitlementReport(String userId); List<Group> membershipReport(String userId); boolean entitle(Group group, Entitlement entitlement); boolean entitle(String userId, Entitlement entitlement); boolean add(String userId, Group group); boolean remove(String userId, Group group); boolean remove(String userId, Entitlement entitlement); boolean remove(Group group, Entitlement entitlement); }
  37. 37. Example: Segregating Interfaces public interface EntitlementsModifier { boolean entitle(Group group, Entitlement entitlement); boolean entitle(String userId, Entitlement entitlement); boolean add(String userId, Group group); boolean remove(String userId, Group group); boolean remove(String userId, Entitlement entitlement); boolean remove(Group group, Entitlement entitlement); } public interface EntitlementsChecker { boolean isAuthorised(String userId, Resource resource, Action action); List<Entitlement> entitlementReport(String userId); List<Group> membershipReport(String userId); }
  38. 38. 5 Dependency Inversion Principle
  39. 39. Dependency Inversion Principle ● High-level modules should not depend on low- level modules. – Both should depend on abstractions. ● Abstractions should not depend on details. – Details should depend on abstractions. ● Dependencies are a risk, – Details bind dependants to concrete implementations. ● This limits flexibility and extensibility. – Abstractions provides independence to dependants. ● Dependants are able to select most suitable implementations.
  40. 40. Example: Averaging Calculator ● A class that uses – An averaging algorithm to process input. – A parser to parse text input to a collection of doubles. – Input and output classes. ● Input formats can change. – Binding the calculator to a concrete parser and IO makes it inflexible. – Averaging calculator references a parser, an input and an output interface. – The application specifies the parser and IO needed.
  41. 41. Example: Parser public interface IParser { List<Double> parse(final String data); } public abstract class Parser implements IParser { public List<Double> parse(final String data) { final String[] parsedData = split(data); return Arrays.asList(parsedData).stream(). map(item -> Double.parseDouble(item)). collect(Collectors.toList()); } protected abstract String[] split(final String data); }
  42. 42. Example: Input public interface IInput { String read(); } public class FixedInput implements IInput { private final String data; public FixedInput(final String data) { this.data = data; } @Override public String read() { return data; } }
  43. 43. Example: Output public interface IOutput { void write(final List<Double> data, final double average); } public class ConsoleOutput implements IOutput { @Override public void write(final List<Double> data, final double average) { System.out.printf("Average%s = %fn", data, average); } }
  44. 44. Example: Mean Processor public interface IProcessor { double process(List<Double> data); } public class MeanProcessor implements IProcessor { public double process(final List<Double> data) { return data.stream(). collect(Collectors. averagingDouble(item -> item)); } }
  45. 45. Example: Averaging Calculator public class AveragingCalculator { final private IInput input; final private IOutput output; final private IProcessor algo; final private IParser parser; public AveragingCalculator(final IProcessor algo, final IParser parser, final IInput input, final IOutput output) { this.algo = algo; this.parser = parser; this.input = input; this.output = output; } public void run() { final String inputData = input.read(); final List<Double> data = parser.parse(inputData); final double average = algo.process(data); output.write(data, average); } }
  46. 46. Example: CSV Averaging App public class CsvAveragingApp { public static void main(String[] args) { final IInput input = new FixedInput(args[0]); final IOutput output = new ConsoleOutput(); final IParser parser = new CsvParser(); final IProcessor algo = new MeanProcessor(); final AveragingCalculator calc = new AveragingCalculator(algo, parser, input, Output); calc.run(); } }
  47. 47. Example: XML Averaging App public class XmlAveragingApp { public static void main(String[] args) { final IInput input = new FixedInput(args[0]); final IOutput output = new ConsoleOutput(); final IParser parser = new XmlParser(); final IProcessor algo = new MeanProcessor(); final AveragingCalculator calc = new AveragingCalculator(algo, parser, input, output); calc.run(); } }
  48. 48. Opposite of SOLID Single Responsibility Principle Open Close Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle
  49. 49. Opposite of SOLID Single Responsibility Principle Open Close Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle S T U P I D
  50. 50. Opposite of SOLID Single Responsibility Principle Open Close Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle Singletons Tight Coupling Untestability Premature Optimisation Indescriptive Naming Duplication

×