Software Design Principles
Software Design PrinciplesPrefaceConcept of ContractsDesign PrinciplesSolid Design PrincipleSingle ResponsibilityOpen/Close PrinciplesLiskov substitution principleInterface segregation PrinciplesDependency Inversion PrincipleDesign by contractInversion of ControlDependency injectionDependency injection SolutionSummaryDI framework for Java.Dragger2Design patterns in Java and C++Design #1 Singleton design patternsDesign #2 Factory method patternsDesign #3 Abstrat factoryDesign #4 Builder PatternsDesign #5 Prototype patternsDesign #6 Adapter PatternsDesign #7 Bridge Design patternsDesign #8 Composite design patternsDesign #9 Decorator patternsDesign #10 Façade patternsDesign #11 FlyWeight patternsDesign #12 Proxy patternsDesign #13 Chain of ResposibilityDesign #14 Command patternsDesign #15 Interpreter PatternsDesign #16 Iterator patternDesign #17. Mediator PatternsDesign #18 Mememto patternsDesign #19 Observer patternsDesign #20. State PatternsDesign #21 Template Method patternsDesign #22 Visitor patternsJavaScript Design PattersDesign #1: Constractor patterns ( JS)Design #2: Module Patterns for Javascript.Python Design PattersAndroid Design PattersDesign a Custom ViewDesign Better List View
Software Design Principles
Preface
I am writing this book to share some software design principles which I have learnt so far in my past 7 years of industry experiences. This books mainly deals with some of the important design patterns, system design problem and handling dependency issues.
I remember, I wrote my first computer software in my 3rd year of my college – it was just a couples of code files. There is no separation of business logic, UI, database interaction etc. However, it just works. As we keep getting new requirement, the code base become large and complex – also difficult to maintain. This book provides a clean guideline on how to write a complex software – which is easy to maintain, extend by adding new feature or unplug some existing feature. This will help us to reduce bugs on the product, write a good test case and so on.
The book is not only providing design guideline, but it is also important to understand why this guideline is important – and what can happen if you not use this design. I will try my best to explain the difference between “good design” vs not so-good design. Ideally, there is no perfect design for a given problem – all it depends on the “nature of the problem” and the context.
This book is example centric – that means you will find the code of “right design” and “not so good design”.
So, let’s start the journey to have a better design.
Concept of Contracts
As per Wikipedia, A contract is a promise or set of promises that are legally enforceable and, if violated, allow the injured party access to legal remedies. Contract law recognizes and governs the rights and duties arising from agreements.
When we build a large software product, it builds with a number of induvial components. Even if they are independent, they need to talk to each other to build the final product. For example:
  1. if you want to develop a e-comm software – we can have server-side and client-side component which can be build up independently – however, how they can commutate to each other can be define by a set of REST API calls – which is a contacts between these two components.
  2. If you have two modules of a product and if there is necessary of calling each other – we can have a contact on how they can communicate. Interface (in case of java), abstract class(in C++) and header file(In c) is mainly treated as a contacts. Other component should only know the contacts (method signature like input and output) – the agreement on communication.
Design Principles
Solid Design Principle
When you design a software in object oriented programming, you shoud always follow SOLID Principles. It's nothing but a collection of 5 design principles to make a software design more understandable, maintenance and flexiable. It includes:
  1. Single responsibility principle - which means a class should have only a single responsibility. The class should be minimal and do one job in its best.
  2. Open/closed principle: Class should be open for extension, but closed for modification
  3. Liskov substitution principle: We should build something "design by contract" not by impl which allows objects in a program can be replaceable with instances of their subtypes without altering the correctness of that program.
  4. Interface segregation principle : many client-specific interfaces are better than one general-purpose interface.
Dependency inversion principle: Your class design should depend upon abstractions, not on concretions/ implementation.
Single Responsibility
Single responsibility indicates that your "Class should be doing one and only one specific thing and not trying to do more than it should. This is called called High Cohesion.
Some simple example is as below: We have class called person which contains email and inside the below class, we are trying to validate the email - which should be the responsibility of that class.
(expand code)
class Person { public name : string; public surname : string; public email : string; constructor(name : string, surname : string, email : string){ this.surname = surname; this.name = name; if(this.validateEmail(email)) { this.email = email; } else { throw new Error("Invalid email!"); } } validateEmail(email : string) { var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i; return re.test(email); } greet() { alert("Hi!"); }
It should be design like this: It can be noted that definition of Person class is no clear now.
(expand code)
class Email { public email : string; constructor(email : string){ if(this.validateEmail(email)) { this.email = email; } else { throw new Error("Invalid email!"); } } validateEmail(email : string) { var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i; return re.test(email); } } class Person { public name : string; public surname : string; public email : Email; constructor(name : string, surname : string, email : Email){ this.email = email; this.name = name; this.surname = surname; } greet() { alert("Hi!"); } }
Single responsibility principle a most effective way to avoid having a GOD classes in your application. That is classes that keeps track of a lot of information and have several responsibilities.
Open/Close Principles
open/closed principle states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification". Let's discuss what does it means by example:
Let's assume we have a collections of shapes ( like rectange and circle) and we want to find out the sum of area of the all elements in the shape collation. How do you solve the problem?
You might have a Rectangle and Circle Class and then Have their own AreaCalculator like below
(expand code)
public class Rectangle { public double Width { get; set; } public double Height { get; set; } } public class Circle { public double Width { get; set; } public double Height { get; set; } } public double AreaCalculator(object[] shapes) { double area = 0; foreach (var shape in shapes) { if (shape is Rectangle) { Rectangle rectangle = (Rectangle) shape; area += rectangle.Width*rectangle.Height; } else { Circle circle = (Circle)shape; area += circle.Radius * circle.Radius * Math.PI; } } return area; }
Now if we add some more shape like say trangle, your logic in AreaCalculator needs to be changed. That means your design is not open to support new features ( like new shapes). In other word, AreaCalculator is NOT "closed" for modification and NOT "open" for extension.
So, how can we have a AreaCalculator which is open for extension and closed for modication ? The Problem of AreaCalculator is - it depends on concrete implementation of shape- it it depends on a "contracts" like an interface should solve that problem as below:
(expand code)
public abstract class Shape { public abstract double Area(); } public class Rectangle : Shape { public double Width { get; set; } public double Height { get; set; } public override double Area() { return Width*Height; } } public class Circle : Shape { public double Radius { get; set; } public override double Area() { return Radius*Radius*Math.PI; } } public double AreaCalculator(Shape[] shapes) { double area = 0; foreach (var shape in shapes) { area += shape.Area(); } return area; }
Now the above AreaCalculator depends on shape and if you need to add any more share - you need to create trangle which implements Shape. In other words we’ve closed it for modification by opening it up for extension.
Liskov substitution principle
This principle is little-bit complicated. This is based on the relationship between Base calss and Derived class or an Interface and it's all implementation. As per Wikipedia it says "if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of the program (correctness, task performed, etc.)" - That means all the derive class objects - must meets all the requements by base class ( or by interface). If it doent, then we say that LSP is getting violated.
In general, you might think that how it is possible. But yes it might be possible to violate the LSP. Let's consider the below example:
In mathematics, a Square is a Rectangle. As "IS-A" is an inheritance:Rectangle is a base class and Square is derive Rectangle. Right? No It's violate LSP.
Let's define the function.
(expand code)
class Rectangle { int getHeight() void setHeight(int value) int getWidth() void setWidth(int value) } class Square : Rectangle { }
We wrote some test case/ invariant for rectangle as below:
(expand code)
void invariant(Rectangle r) { r.setHeight(200) r.setWidth(100) assert(r.getHeight() == 200 and r.getWidth() == 100) }
Now if I create an objcet of Squere and as it is a type of Rectangle too. it should pass the invariant - but it is not! That means it violates LSP. In fact, This invariant must be violated by a correct implementation of Square, therefore it is not a valid substitute of Rectangle.
Now next question is "why it violates LSP?". This is because of Square tighten the constrains which might break the asserts of Rectangle. It also means Square doesn't respect the right rule set by Rectangle.
Another example of violation is as below: We have IVehicle class which asserts the possible miles. We define another class Scooter but as it tighten the assert which violate LPS. this is a violation where a "pre condition is strengthened by a subtype". Similarly, post conditions may not be weakened (i.e. relaxed) by a subtype, else it violate LSP.
(expand code)
public class IVehicle { public virtual void Drive(int miles) { Assert(miles > 0 && miles < 300); // Consumers see this as the contract } } public class Scooter : IVehicle { public override void Drive(int miles) { Assert(miles > 0 && miles < 50); // ** Violation base.Drive(miles); } }
There are many ways where we can violates the LSP:
  1. Glaring Violation - Runtime type switching
(expand code)
void MethodWhichViolatesLSP(IVehicle aVehicle) { if (aVehicle is Car) { var car = aVehicle as Car; // Do something special for car - this method is not on the IVehicle interface car.ChangeGear(); }
  1. Violation - Pre condition is strengthened by a subtype
(expand code)
public class Vehicle { public virtual void Drive(int miles) { Assert(miles > 0 && miles < 300); // Consumers see this as the contract } } public class Scooter : Vehicle { public override void Drive(int miles) { Assert(miles > 0 && miles < 50); // ** Violation base.Drive(miles); } }
  1. Subtle Violation - Abuse of an interface implementation by a subclass
Interface segregation Principles
The interface-segregation principle (ISP) states that "Many client specific interfaces are better than one general purpose interface" , in other word - "no client should be forced to depend on methods it does not use". It helps with creating thin/ small abstraction interfaces that make it easy for clients to have fewer dependant factors between them.
For example, we are trying to builds and interface of an byte storage operation. it can be design as below:
(expand code)
type ByteUtils interface { Read(b []byte) (n int, err error) // Read into buffer Write(b []byte)(n int, err error) // Write into buffer Trim(b []byte, exclusions string)[]byte // Trim buffer by removing bytes from the exclusion chars }
It works for a while, However notice that the name ByteUtils is too generic. If some client only wants to do Read Operation, it force to implement all of the methods. A better design would be like:
(expand code)
type Reader interface { Read(b []byte) (n int, err error) } type Writer interface { Write(b []byte)(n int, err error) } type Trimmer interface { Trim(b []byte, exclusions string)[]byte }
Such thin interfaces are also called role interfaces as its easier to refactor, change, and redeploy something that it has a very defined role or purpose.
Another choise to have combined one:
(expand code)
type ReadWriter interface { Reader Writer } type TrimReader interface { Trimmer Reader }
We can also use "extending Interface" to support this principle:
(expand code)
public interface Door { void Lock(); void Unlock(); bool IsDoorOpen(); } public interface TimedDoor : Door { void DoorTimeOut(int timeOutId); }
Dependency Inversion Principle
DIP is defined as "Depend upon Abstractions. Do not depend upon concretions" OR
" Abstractions should not depend upon details. Details should depend upon abstractions" OR "High-level modules should not depend on low-level modules. Both should depend on abstractions".
Let;s consider we have two modules of an application: Module1 and Module2. Now suppose Module1 depends on Module2. How it depends? A class in Module1( say ClassA) depens on ClassB ( which is in module2). This is called hard dependency. It suffers from below proble:
A better way to do that,
Let's consider one example. We have a module which takes worker from external and execute the work method. Module1 has a manager - which has Worker object and then execute it. As It conatins the class object - it's a hard dependency on module2 wehere Worker is defined.
(expand code)
// module 1 class Manager { Worker worker; <<<< This a hard dependency public void setWorker(Worker w) { worker = w; } public void manage() { worker.work(); } } // Module 2 class Worker { public void work() { // ....working } }
The correct way to implement things as below: Here Module1 is not depends on module2 but depends on it's own interface. where as Module2 acts as an worker provider to Module1 and so depends on module 1. So Invert the dependecny.
If we want to make Module1 and Module2 Indipendent, we should move the interface to module3, and module1 and module2 depends on module3.
(expand code)
// Module 1 interface IWorker { public void work(); } class Manager { IWorker worker; public void setWorker(IWorker w) { worker = w; } public void manage() { worker.work(); } } // Module2 class Worker implements IWorker{ public void work() { // ....working } } // Module 3 class SuperWorker implements IWorker{ public void work() { //.... working much more } }
Design by contract
Inversion of Control
When we implements a class it takes a lot of hard dependency ( means takes a lot of concrete objects as a part of the objects). Using IoC, the modules receive the dependency from a generic framework or from outside, which allow the modules to be indipended on creation of the dependent objets.
The term is similar to the dependency inversion principle, which concerns itself with decoupling dependencies between high-level and low-level layers through shared abstractions however, it tells on how you will get the dependent object from outside world - mostly using some inetrafce.
Inversion of control serves the following design purposes:
There are many techniques to implement inversion of control, which inclues:
  1. Using a service locator pattern:
  2. Using dependency injection - like passing( Inject ) the dependency from outside
  1. Using a contextualized lookup
  2. Using template method design pattern
  3. Using strategy design pattern
Dependency injection
Dependency injection is a technique by which we can inject an object to another object.
For exaple, you have two class say : ClassA and Class B. class A needs classB to get it's job done - that means ClassA depends on classB. Instaed of having this hard dependency, ClassA depends on the interfaceB and the concrete implementing is supplied from outside( propably asking some thrird party libs to create and return back the instance)
There are three common means for a client to accept a dependency injection: setter-, interface- and constructor-based injection as given below:
(expand code)
// Without DI public class Client { private ExampleService service; <<< Hard Dependency Client() { service = new ExampleService(); <<< Creation is my resposibilty. } public String greet() { return "Hello " + service.getName(); } } // Using Constructor injection public class Client { private ISerive service; <<< Dependency on Interface Client(IService service) { this.service = service; } public String greet() { return "Hello " + service.getName(); } } // Setter injection public class Client { private ISerive service; <<< Dependency on Interface void setService(IService service) { this.service = service; } public String greet() { return "Hello " + service.getName(); } } //Interface injection public interface ServiceSetter { <<< We have a Setter Interface public void setService(IService service); } // Clients implements this contracts public class Client implements ServiceSetter { // Internal reference to the service used by this client. private IService service; @Override <<< This will gurentee that we have a set methods public void setService(Service service) { this.service = service; } }
Dependency injection Solution
Summary
DI framework for Java.
In this section, we will explore some of DI framework. Infact, you don't need the framework and It is totally possible to implement dependency injection manually. However, using a framework makes it easier. Particularly one based on annotations or automatic detection of dependencies, as it makes the process simpler. They help us by
  1. They help you wiring up complex object relationships. You have to write no boilerplate code, to generate instances and pass them to the appropriate objects
  2. They help you control when an object is created: you could create instances during application bootstrapping, but in some cases a lazy creation - only when needed - is better
  3. They help you control how many instances are created: one per application lifetime or one per request (in case you are doing web-programming).
In this sesction we will investigate two mostly used DI framework called: Dagger2 and Juces
Dragger2
Dagger 2 is dependency injection framework which uses code generation and is based on annotations. Dagger2 is not:
  1. NOT a dependency injection - however it's just an anotations processor which generate codes which is used for DI. Itexposes a number of special annotations: like
  1. NOT a not provides dependency on it's own. You needs to provides them.
You can setup dagger in android application by spacing the annotation processor and the libs itself as below:
(expand code)
// app/build.gradle apply plugin: 'com.android.application' android { .... } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' testCompile 'junit:junit:4.12' compile "com.google.dagger:dagger:2.8" <<< Needed annotationProcessor "com.google.dagger:dagger-compiler:2.8" <<< define processsor provided 'javax.annotation:jsr250-api:1.0' <<< USE JSR compile 'javax.inject:javax.inject:1' <<< USE INJECT }
As we discussed before DI is builds on two compoennet :
  1. Providers - Where you need to spacify some rule on how to instatiate class when DI fragemnet asks for a implementation. It can be done using @Module, @Provides and @Component annotation. In If Classes annotated with @Module then they are responsible for providing objects which can be injected. Such classes can have some method which provides instnace. thease method can be annotated with @Provides to indicate that these method will be called when you ask for. That means the returned objects from these methods are available for dependency injection. Methods annotated with @Provides can also express dependencies via method parameters. These dependencies are fulfilled by Dagger 2, if possible. We can also spacify scope lile @Singleton to indicate this instance of this provided object is created and shared. We also needs to define a interface annotated with @Component. This interface is used by Dagger2 to generate code which uses the modules to fulfill the requested dependencies. Component combined all the modules as a single interface to outside.
  2. Consumer: If some classA needs some instnace of ClassB, it can use @Inject annotation to define a dependency. The classA can depens on classB by 3 Way: Class B defined as class field or defined inside method or passes a paremetr to it;s method. That means we need a support of 3 way to inject a dependency in dagger. Here is the example of all three way to inject:
(expand code)
// Vehicle needs Motor, Motor instance will be supplied by Dagger @Inject public Vehicle(Motor motor){ this.motor = motor; }
To use DI, your app must initilized Compoenent like
(expand code)
AppComponent = DaggerAppComponent.builder() .appModule(AppModule(app)) ... .build()
Let's see using an example:
Let's we have User Class and a BackendService Class which provides User and other parameters. So we need to have actual Class Impl
(expand code)
public class User { private String firstName; private String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } @Override public String toString() { return "User [firstName=" + firstName + ", lastName=" + lastName + "]"; } } public class BackendService { @Inject public User user; <<< It will be injected as fields. private String serverUrl; @Inject public BackendService(@Named("serverUrl") String serverUrl) { <<< Injected as params this.serverUrl = serverUrl; } public boolean callServer() { if (user !=null && serverUrl!=null && serverUrl.length()>0) { System.out.println("User: " + user + " ServerUrl: " + serverUrl); return true; } return false; } }
To support the injection on User and serverUrl, we needs to define module and Providers( which supplies the concreate objects.)
(expand code)
import dagger.Module; import dagger.Provides; @Module public class BackEndServiceModule { @Provides @Singleton BackendService provideBackendService(@Named("serverUrl") String serverUrl) { return new BackendService(serverUrl); } @Provides @Named("serverUrl") String provideServerUrl() { return "http://www.dipankar.co.in"; } @Provides @Named("anotherUrl") String provideAnotherUrl() { return "http://www.google.com"; } } @Module public class UserModule { @Provides @Singleton User providesUser() { return new User("Dipankar", "Dutta"); } }
Now we need to have a componenet which will link then.
(expand code)
import javax.inject.Singleton; import com.vogella.java.dagger2.BackendService; import com.vogella.java.dagger2.modules.BackEndServiceModule; import com.vogella.java.dagger2.modules.UserModule; import dagger.Component; @Singleton @Component(modules = { UserModule.class, BackEndServiceModule.class }) public interface MyComponent { // provide the dependency for dependent components // (not needed for single-component setups) BackendService provideBackendService(); // allow to inject into our Main class // method name not important void inject(Main main); }
Now, here is the client code which use DI.
(expand code)
package com.vogella.java.dagger2.main; import com.vogella.java.dagger2.BackendService; import com.vogella.java.dagger2.component.DaggerMyComponent; import com.vogella.java.dagger2.component.MyComponent; public class Main { @Inject BackendService backendService; // This is Injcted private MyComponent component; private Main() { component = DaggerMyComponent.builder().build(); component.inject(this); } private void callServer() { boolean callServer = backendService.callServer(); if (callServer) { System.out.println("Server call was successful. "); } else { System.out.println("Server call failed. "); } } public static void main(String[] args) { Main main = new Main(); main.callServer(); } }
If you want to convert an existing project to use DI, do the following:
  1. Identify the dependent objects and its dependencies.
  2. Associate a class with the @Module annotation, using the @Provides annotation for every method that returns a dependency.
  3. Request dependencies in your dependent objects using the @Inject annotation.
  4. Create an interface using the @Component annotation and add the classes with the @Module annotation created in the second step.
  5. Create an object of the @Component interface to instantiate the dependent object with its dependencies.
Design patterns in Java and C++
Design #1 Singleton design patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
//Solsution:1 Cleated on class load time.
(expand code)
class SingletonEarly { private static SingletonEarly singleton = new SingletonEarly( ); private SingletonEarly() { System.out.println("SingletonEarly: Contracted "); } public static SingletonEarly getInstance( ) { return singleton; } }
//Solsution 2: Deferred singleton,.
(expand code)
class SingletonDelay { private static SingletonDelay instance = null; private SingletonDelay() { System.out.println("SingletonDelay: Contracted "); } public static SingletonDelay getInstance( ) { // double lock tech - This check ensure queue if(instance == null) { synchronized(SingletonDelay.class) { // this lock protect the instance if (instance == null) { instance = new SingletonDelay(); } } } return instance; } }
Design #2 Factory method patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Factory pattern : To to crete simailr object based on some argument ype. Clinet will abstarcted how they build this.
(expand code)
interface IShape { void draw(); } class Rectangle implements IShape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } class Square implements IShape { @Override public void draw() { System.out.println("Inside Square::draw() method."); } } enum ShapeType { RECT, SQR; } class ShapeFactory { public static IShape getShape(ShapeType shapeType) { switch(shapeType) { case RECT : { return new Rectangle(); } case SQR : { return new Square(); } default : { return null; } } } }
Design #3 Abstrat factory
Example:
Design purpose:
Design challenges:
Summery of designs:
(expand code)
abstract class CreditCard { protected int cardNumberLength, csv; } enum CardType { GOLD, PLATINUM; } class AmexPlatinumCreditCard extends CreditCard {} class AmexGoldCreditCard extends CreditCard {} class VisaBlackCreditCard extends CreditCard {} class VisaGoldCreditCard extends CreditCard {} abstract class CreditCardFactory { public static CreditCardFactory getCreditCardFactory(int creditScore) { if(creditScore > 650) { return new AmexFactory(); } else { return new VisaFactory(); } } public abstract CreditCard getCreditCard(CardType cardType); } class AmexFactory extends CreditCardFactory { @Override public CreditCard getCreditCard(CardType cardType) { switch (cardType) { case GOLD : return new AmexGoldCreditCard(); case PLATINUM : return new AmexPlatinumCreditCard(); default : break; } return null; } } class VisaFactory extends CreditCardFactory { @Override public CreditCard getCreditCard(CardType cardType) { switch (cardType) { case GOLD : return new VisaGoldCreditCard(); case PLATINUM : return new VisaBlackCreditCard(); } return null; } }
Design #4 Builder Patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Let's consider the below classic example:
(expand code)
class LunchOrder { //now we need to have a builder note that inner class return the builder object public static class Builder { private String bread; private String dressing; private String meat; public Builder() { } public LunchOrder build() { return new LunchOrder(this); } public Builder bread(String bread) { this.bread = bread; return this; } public Builder dressing(String dressing) { this.dressing = dressing; return this; } public Builder meat(String meat) { this.meat = meat; return this; } } //That that the item can't be chnages ahere aas they are final. private final String bread; private final String dressing; private final String meat; private LunchOrder(Builder builder) { this.bread = builder.bread; this.dressing = builder.dressing; this.meat = builder.meat; } public String getBread() { return bread; } public String getDressing() { return dressing; } public String getMeat() { return meat; } public void print() { System.out.println("bread:" + bread + ", dressing:" + dressing + ", meat:" + meat); } }
Let's consider the below example of having a logger Builder patetrns
(expand code)
//setups new Logger.Builder() .isLoggable(BuildConfig.DEBUG) .logType(LogType.WARN) .tag("MyTag") .setIsKotlin(true) .build(); } //Usees Logger.log("Log Message"); Logger.i("Info Message"); Logger.e("Error Message"); Logger.w("Warn Message"); Logger.d("Debug Message"); Logger.e(null); Logger.e("Error Message with ThrowAble", new Throwable("Some Error"));
This is how we define the lib using builder patterns:
First we Define the class Logger with private contractor and all the peremetr to setup. Now, we have internal static class called builder, which have all the paremetr again as private and alows set and get. You should not that the setter returns this to allows chain setting, at end we call build() function in which we call init() function of outer class. init function copy all the peremeter from Build class to Logger class to initlilize. As Logger class donet have any setter, we can't change any paremetr after building. Now we imaplement all the supported API for Logger class as a static veriable.
(expand code)
public class Logger { private Logger() { throw new RuntimeException("Private constructor cannot be accessed"); } private static LogType logType = LogType.INFO; private static boolean isLoggable = true; private static boolean isKotlin = false; private static String TAG = "Logger"; //inner static class builder public static class Builder { private static LogType logType = LogType.INFO; private static boolean isLoggable = true; private static boolean isKotlin = false; private static String tag = "Logger"; public Builder logType(LogType logType) { Builder.logType = logType; return this; } public Builder isLoggable(boolean isLoggable) { Builder.isLoggable = isLoggable; return this; } public Builder tag(String tag) { Builder.tag = tag; return this; } public Builder setIsKotlin(boolean isKotlin) { Builder.isKotlin = isKotlin; return this; } public boolean isIsKotlin() { return isKotlin; } LogType getLogType() { return logType; } boolean isIsLoggable() { return isLoggable; } String getTag() { return tag; } public void build() { init(this); } } //build will call this and copy the paremetr, private static void init(Builder builder) { Logger.logType = builder.getLogType(); Logger.TAG = builder.getTag(); Logger.isLoggable = builder.isIsLoggable(); Logger.isKotlin = builder.isIsKotlin(); } //APIs public static void e(Object message) { if (isLoggable) { Log.e(TAG, "| " + makeLog(message, "e")); } } public static void e(Object message, Throwable throwable) { if (isLoggable) { Log.e(TAG, "| " + makeLog(message, "e"), throwable); } } public static void i(Object message) { if (isLoggable) { Log.i(TAG, "| " + makeLog(message, "i")); } } public static void w(Object message) { if (isLoggable) { Log.w(TAG, "| " + makeLog(message, "w")); } } public static void d(Object message) { if (isLoggable) Log.d(TAG, "| " + makeLog(message, "d")); } public static void log(Object message) { if (isLoggable) { String body = "| " + makeLog(message, "log"); switch (logType) { case INFO: Log.i(TAG, body); break; case DEBUG: Log.d(TAG, body); break; case ERROR: Log.e(TAG, body); break; case WARN: Log.w(TAG, body); break; } } } private static String makeLog(Object message, String calledMethodName) { StackTraceElement[] stackTraceElement = Thread.currentThread().getStackTrace(); int currentIndex = -1; for (int i = 0; i < stackTraceElement.length; i++) { if (stackTraceElement[i].getMethodName().compareTo(calledMethodName) == 0) { currentIndex = ++i; break; } } StackTraceElement traceElement = Thread.currentThread().getStackTrace()[currentIndex]; String fullClassName = traceElement.getClassName(); String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1); String methodName = traceElement.getMethodName(); int lineNumber = traceElement.getLineNumber(); String logMessage = message == null ? null : message.toString(); String postFix = isKotlin ? ".kt:" : ".java:"; return logMessage + " | (" + className + postFix + lineNumber + ")"; } }
Design #5 Prototype patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
(expand code)
abstract class Item implements Cloneable { private String title; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } class Book extends Item { private int numberOfPages; public int getNumberOfPages() { return numberOfPages; } } class Movie extends Item { private String runtime; public String getRuntime() { return runtime; } public void setRuntime(String runtime) { this.runtime = runtime; } } //We need to registry to store the prototype. class Registry { private Map < String, Item > items = new HashMap < String, Item > (); public Registry() { loadItems(); } public Item createItem (String type) { Item item = null; try { // note that we are not using new but clone. item = (Item)(items.get(type)).clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return item; } private void loadItems() { Movie movie = new Movie(); movie.setTitle("Basic Movie"); items.put("Movie", movie); Book book = new Book(); book.setTitle("Basic Book"); items.put("Book", book); } }
Design #6 Adapter Patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #7 Bridge Design patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #8 Composite design patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #9 Decorator patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #10 Façade patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #11 FlyWeight patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #12 Proxy patterns
Example:
Design purpose:
-- Extra level of indirection to support distributed, controlled, or intelligent access
-- Security to protect the real component from undue complexity
-- To provides stubs for testing
-- Impove memeory uses using caching
-- Aavoid duplicate remote calls.
- Use an extra level of indirection to support distributed, controlled, or intelligent access.
Design challenges:
Summery of designs:
Design #13 Chain of Resposibility
Example:
Design purpose:
Design challenges:
Summery of designs:
Diagram:
Design #14 Command patterns
Example:
Design purpose:
-- Recever – A complex system which supports many comlex UI
-- Request – calling each function in receiver is treaded as request.
-- ICommand – is a contarct which needs to be implemented for reach request.
-- Concreate commands – is a object for each request which delegate the calls to recever after checking the state of receiver.
-- Invoker - Client uses the invoker to issue the command.
Design challenges:
Summery of designs:
Design #15 Interpreter Patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #16 Iterator pattern
Example:
Design purpose:
Design challenges:
Summary of designs:
Design #17. Mediator Patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #18 Mememto patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #19 Observer patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #20. State Patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #21 Template Method patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
Design #22 Visitor patterns
Example:
Design purpose:
Design challenges:
Summery of designs:
JavaScript Design Patters
Design #1: Constractor patterns ( JS)
Example:
Design purpose:
Design challenges:
Summery of designs:
Example1: Basic Notion of object
(expand code)
var Task = function (name) { this.name = name; this.completed = false; this.complete = function () { console.log('completing task: ' + this.name); this.completed = true; }; } Var ob1 = new Task(“Dipankar”)
// Example 2: using prototype : the function is not copied.
(expand code)
var Task = function (name) { this.name = name; this.completed = false; } Task.prototype.complete = function () { console.log('completing task: ' + this.name); this.completed = true; }; Task.prototype.save = function () { console.log('saving Task: ' + this.name); };
//Example 3: Uisng defineProperty – Which give more protection of memebrer function.
(expand code)
var task = { title: 'My Title', description: 'My Description' }; Object.defineProperty(task, 'toString', { value: function () { return this.title + ' ' + this.description; }, writable: false, enumerable: false, configurable: false });
//Example 4: use strict – A modern way to write JS calss
(expand code)
'use strict' class Task { constructor(name) { this.name = name; this.completed = false; }; complete() { console.log('completing task: ' + this.name); this.completed = true; }; save() { console.log('saving Task: ' + this.name); }; } var task1 = new Task('create a demo for constructors'); var task2 = new Task('create a demo for modules'); task1.complete(); task2.save();
Design #2: Module Patterns for Javascript.
Example:
Design purpose:
Design challenges:
Summery of designs:
// Example 1: Basic Module Structre
(expand code)
var Module = { method: function(){...}, nextMethod: function(){...} }
//Example 2: Real Module Patterns with private memebr
(expand code)
var Module = function () { var privateVar = 'I am private...'; return { method: function () {... }, nextMethod: function () {... } } }
//Example 3: Better Impl, Private veribale and private function.
(expand code)
var repo = function () { var db = {}; var get = function (id) { console.log('Getting task ' + id); return { name: 'new task from db' } } var save = function (task) { console.log('Saving ' + task.name + ' to the db'); } return { get: get, save: save } }
Python Design Patters
Android Design Patters
Design a Custom View
(expand code)
public class CustomView extends RelativeLayout { @Nullable private Callback mCallback; private Button mButton; public interface Callback { void onClick(); } public CustomView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public CustomView(Context context) { super(context); initView(context); } private void initView(Context context) { final LayoutInflater mInflater = LayoutInflater.from(context); mInflater.inflate(R.layout.view_notification, this, true); mButton = findViewById(R.id.button); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(mCallback != null){ mCallback.onClick(); } } }); } public void addCallback(Callback callback){ mCallback = callback; } }
Design Better List View
Suppose I want to have a List Items like this which should be listed
(expand code)
public interface Item{ String getTitle(); String getSubTitle(); String getImageUrl(); String getId(); }
and Here is the layout for each item:
(expand code)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:paddingTop="8dp" android:paddingBottom="8dp" android:paddingLeft="5dp" android:paddingRight="5dp" android:layout_width="wrap_content" android:gravity="center" android:layout_height="wrap_content"> <in.co.dipankar.quickandorid.views.CircleImageView android:id="@+id/image" android:layout_width="40dp" android:background="@null" android:src="@drawable/ic_user_male" android:layout_gravity="center" app:civ_border_color="#15000000" app:civ_border_width="1dp" android:layout_marginBottom="4dp" android:layout_height="40dp" /> <TextView android:id="@+id/sl" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/subtitle" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
Here is the Adapter
(expand code)
private class RVAdapter extends RecyclerView.Adapter<RVAdapter.ItemViewHolder> { List<Item> nodes = new ArrayList<>(); Context mContext; int mItemLayout; RVAdapter(List<Item> persons, Context c,int mItemLayout) { if (persons != null) { this.nodes = persons; } mContext = c; this.mItemLayout = mItemLayout; } // define Holder class public class ItemViewHolder extends RecyclerView.ViewHolder { TextView sl; TextView title; TextView subtitle; ImageView img; ItemViewHolder(View itemView) { super(itemView); img = (ImageView) itemView.findViewById(R.id.image); sl = (TextView) itemView.findViewById(R.id.sl); title = (TextView) itemView.findViewById(R.id.title); subtitle = (TextView) itemView.findViewById(R.id.subtitle); } } @Override public int getItemCount() { return nodes.size(); } // Influate View @Override public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_quick_list, viewGroup, false); ItemViewHolder pvh = new ItemViewHolder(view); return pvh; } // Populate views @Override public void onBindViewHolder(ItemViewHolder personViewHolder, int i) { if(personViewHolder.title != null) { personViewHolder.title.setText(nodes.get(i).getTitle()); } if(personViewHolder.sl != null) { personViewHolder.sl.setText((i+1)+""); } if(personViewHolder.subtitle != null) { personViewHolder.subtitle.setText(Html.fromHtml(nodes.get(i).getSubTitle()), TextView.BufferType.SPANNABLE); } if(personViewHolder.img != null) { Glide.with(mContext) .load(nodes.get(i).getImageUrl()) .into(personViewHolder.img); } } // Update the list in the adapetr public void update(List<Item> datas) { if (datas == null) return; if (nodes != null && nodes.size() >= 0) { nodes.clear(); } nodes.addAll(datas); notifyDataSetChanged(); } }
Let's define a class which allow us to Listn when you click or long tab a item
(expand code)
import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { private OnItemClickListener mListener; public interface OnItemClickListener { public void onItemClick(View view, int position); public void onLongItemClick(View view, int position); } GestureDetector mGestureDetector; public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) { mListener = listener; mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child != null && mListener != null) { mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child)); } } }); } @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { View childView = view.findChildViewUnder(e.getX(), e.getY()); if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { mListener.onItemClick(childView, view.getChildAdapterPosition(childView)); return true; } return false; } @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {} @Override public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){} }
The code must be added to the client side
(expand code)
// Decleare private RecyclerView mRecyclerView; private RVAdapter adapter; // initilize RV View mRootView = mInflater.inflate(R.layout.view_quick_list, this, true); mRecyclerView = (RecyclerView) this.findViewById(R.id.recyclerview); mRecyclerView.setHasFixedSize(true); // Adding layout infra LinearLayoutManager layoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false); LinearLayoutManager layoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false); mRecyclerView.setLayoutManager(layoutManager); // initilization and setting up the adapetr adapter = new RVAdapter(items, mContext); mRecyclerView.setAdapter(adapter); // Adding Touch Listner mRecyclerView.addOnItemTouchListener( new RecyclerItemClickListener(mContext, mRecyclerView ,new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { if(mCallback != null){ mCallback.onClick(mItemList.get(position).getId()); } } @Override public void onLongItemClick(View view, int position) { if(mCallback != null){ mCallback.onLongClick(mItemList.get(position).getId()); } } }) ); // API to update the list public void updateList(List<Item> data){ adapter.update(data); }