A design pattern is a general reusable solution to a commonly occurring problem within software design. It is a description or template for how to solve a problem that can be used in many different situations.
Helps developers reuse successful designs by basing new designs on prior experience.
Improves understandability for developers familiar with the pattern.
Identifies participating classes and instances, their roles and collaborators, and the distribution of responsibilities. This helps ensure adherence to Design Principles.
Creational
Structural
Behavioral
Creation of objects for you rather than instantiating objects directly
Composition of classes and objects to obtain new functionality
Communication and interaction between objects
Encapsulates object creation logic by delegating to implementations of a common Factory interface without specifying the exact class of object to create.
When to use?
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
There are lots of of different types of loggers and which one to use is decided at runtime.
So how does the application determine which logger to use? The separate logger classes shouldn't nor should the classes using the logger.
Here the factory maintains a separation of concerns by isolating the loggers and classes using the loggers from application state.
CreditCardAccountFactory accountFactory = new CreditCardAccountFactory();
foreach (Transaction transaction : customer.getTransactionHistory())
{
AccountType accountType = transaction.getAccountType();
CreditCardAccount account = accountFactory.AccountFactory(accountType);
totalRewards += account.calculateRewards();
}
Factories are useful for creating curtains where systems need to be plug and play or details may not be known at compile time.
the JDBC
The JDBC allows you to interact with various RDBMS without knowing about the specifics of each connector.
At runtime the GetConnection iterates though all of the driver loaded into the JVM (information not know at compile time) and returns one is able to create a connection using the provided URL.
Connection conn = DriverManager.getConnection(URL, USER, PASS);
Solves the "telescoping constructor" problem by encapsulating individual construction steps that can be invoked individually.
When to use?
http
.authorizeRequests()
.antMatchers("/login.jsp").permitAll()
.anyRequest().hasRole("USER")
.and()
.exceptionHandling()
.accessDeniedPage("/login.jsp?authorization_error=true")
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.logoutUrl("/logout")
.logoutSuccessUrl("/login.jsp")
.and()
.formLogin()
.loginProcessingUrl("/login")
.failureUrl("/login.jsp?authentication_error=true")
.loginPage("/login.jsp");
JobDetail job = newJob(HelloJob.class)
.withIdentity("myJob", "group1") // name "myJob", group "group1"
.build();
Trigger trigger = newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(40)
.repeatForever())
.build();
// Tell quartz to schedule the job using our trigger
sched.scheduleJob(job, trigger);
public class Account
{
//Required
private final String accountNumber;
private final String type;
private final String bankDescription;
//Optional
private final String accountStatus;
private final String currencyCode;
private final String customerRole;
private final String currentBalance;
private final String availableBalance;
private final String accountUseType;
public Person(final String accountNumber, final String type,
final String bankDescription, final String accountStatus,
final String currencyCode, final String customerRole,
final String currentBalance, final String availableBalance,
final String accountUseType)
{
...
}
}
public class AccountBuilder
{
//Required
private final String accountNumber;
private final String type;
private final String bankDescription;
//Optional
private final String accountStatus;
...
public AccountBuilder(final String accountNumber, final String type,
final String bankDescription)
{
this.accountNumber = accountNumber;
...
}
public AccountBuilder accountStatus(String accountStatus)
{
this.accountStatus = accountStatus;
return this;
}
...
public createAccount()
{
return new Person(accountNumber, type, bankDescription,
accountStatus, currencyCode, customerRole, currentBalance,
availableBalance, accountUseType);
}
}
final Account account = new Account.AccountBuilder(
"1234123412341234", "Savings", "360")
//The rest are options but simply chain together
.accountStatus("Open")
.createAccount();
The results
Restricts the number of instantiations of a class
to one single instance.
When to use?
When not to use?
You should be able to understand how to use an API without memorizing all of the source code.
testDebitAccount()
{
Account c = new Account("1234123412341234", "360", 5000);
c.debit(200);
assert(c.availableBalance == 4800);
}
In the background there is some stuff going on.
But you can't know this from the API because it lied to you. The API clearly states that you can create an account and then debit it.
testDebitAccount()
{
AccountTransactionQueue.getInstance();
AccountTransactionProcessor.getInstance();
Account c = new Account("1234123412341234", "360", 5000);
c.debit(200);
assert(c.availableBalance == 4800);
}
testDebitAccount()
{
AccountTransactionQueue atQueue =
new AccountTransactionQueue();
AccountTransactionProcessor atProcessor =
new AccountTransactionProcessor(atQueue);
Account c = new Account("1234123412341234", "360", 5000);
atProcessor.debit(c, 200);
assert(c.availableBalance == 4800);
}
Singletons are acceptable when.
A common example is loggers which have a global state but do not impact the execution of the application.
An example of this is environmental variables pulled at the application's launch.
Adapter pattern works as a bridge between two incompatible interfaces. This type of design pattern comes under structural pattern as this pattern combines the capability of two independent interfaces.
class LegacySquarePeg {
private double width;
public SquarePeg(double w) { width = w; }
public double getWidth() { return width; }
public void setWidth(double w) { width = w; }
}
class RoundHole {
private int radius;
public RoundHole( int r ) { radius = r; }
public int getRadius() { return radius; }
}
class SquarePeg {
private LegacySquarePeg sp;
public SquarePegAdapter(double w) {
sp = new LegacySquarePeg(w);
}
public void makeFit(RoundHole rh) {
double amount = sp.getWidth() - rh.getRadius() * Math.sqrt(2);
if (amount > 0) {
sp.setWidth(sp.getWidth() - amount);
}
}
}
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality
Involves a single class which provides simplified
methods required by client and delegates calls to
methods of existing system classes.
Uses a command object to represent the information needed to call a method at a later time. Command information includes the method's name, object that owns the method, and values for the method's parameters.
When to use?