使用一个私有构造方法、一个私有静态变量以及一个公有静态方法来实现。私有构造方法确保了不能通过构造方法来创建对象实例,只能通过公有静态方法返回唯一的私有静态变量。
懒汉式-线程不安全
:私有静态变量被延迟实例化,在多线程环境下不安全,可能多次实例化
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
饿汉式-线程安全
:采取直接实例化的方式就不会产生线程不安全的问题
private static Singleton uniqueInstance = new Singleton();
懒汉式-线程安全
:只需要对getUniqueInstance()
方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次uniqueInstance
。但是当一个线程进入后其他线程必须等待,即使对象已经被实例化,这会让阻塞时间过长,因此该方法有性能问题。
public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
双重校验锁-线程安全
:对象只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化部分的代码进行,只有当uniqueInstance
没有被实例化时,才需要进行加锁。双重校验锁
先判断uniqueInstance
是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
public static getUniqueInstance() {
if (null == uniqueInstance) {
synchronized(Singleton.class) {
if (null == uniqueInstance) {
uniqueInstance = new Singleton();
}
}
}
}
uniqueInstance
采用volatile
关键字修饰也是很有必要的,uniqueInstance = new Singleton();
这段代码其实是分三步执行
uniqueInstance
分配内存空间uniqueInstance
uniqueInstance
指向分配的内存地址‼️ 由于
JVM
具有指令重排
的特性,执行顺序有可能变成1>3>2
。指令重排
在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程T1
执行了1
和3
,此时T2
调用getUniqueInstance()
后发现uniqueInstance
不为空,因此返回uniqueInstance
,但此时uniqueInstance
还未被初始化。
使用volatile可以禁止JVM的指令重排,保证在多线程环境下也能正常运行
静态内部类实现
:当Singleton
类被加载时,静态内部类SingletonHolder
没有被加载进内存。只有当调用getUniqueInstance()
方法从而触发SingletonHolder
.INSTANCE
时SingletonHolder
才会被加载,此时初始化INSTANCE
实例,并且JVM
能确保INSTANCE
只被实例化一次。
public class Singleton() {
private Singleton(){}
private static class SingletonHolder() {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTNACE;
}
}
枚举实现
:可以防止反射攻击。在其他实现中,通过setAccessible()
方法可以将私有构造方法的访问级别设置为public
,然后调用构造方法从而实例化对象,如果要防止这种攻击,需要在构造方法中添加防止多次实例化的代码。该实现是由JVM
保证之后实例化一次,因此不会出现上述的反射攻击。该实现在多次序列化
和反序列化
之后,不会得到多个实例。而其他实现需要使用transient
修饰所有字段,并且实现序列化
和反序列化
的方法。
public enum Singleton {
INSTANCE;
private String objName;
public String getObjName(){
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
}
简单工厂把实例化的操作单独放到一个类,这个类就成为
简单工厂类
,让简单工厂类
来决定应该用哪个具体子类来实例化。这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。
public class SimpleFactory {
public Product createProduct(int type) {
if (type == 1)
return new ConcreateProduct1();
else if (type == 2)
return new ConcreateProduct2();
return new ConcreateProduct3();
}
}
public class Client {
public static void main(String[] args) {
SimpleFactory f = new SimpleFactory();
Product p = f.createProduct(1);
}
}
在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。下图中,Factory有一个doSomething方法,这个方法需要用到一个产品对象,这个产品对象由factoryMethod方法创建。该方法是抽象的,需要由子类去实现。
public abstract class Factory {
abstract public Product factoryMethod();
public void doSomething(){
//
}
}
public class ConcreateFactory extend Factory {
public Product factoryMethod() {
return new ConcreateProduct();
}
}
public class ConcreateFactory1 extends Factory {
public Product factoryMethod() {
return new ConcreateProduct1();
}
}
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。抽象工厂模式用到了工厂方法模式来创建单一对象,AbstractFactory中的createProductA()和createProductB()方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂方法模式的定义。
至于创建对象的家族这一概念是在Client体现,Client要通过AbstractFactory同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client需要同时创建出这两个对象。从高层次来看,抽象工厂使用了组合,即Client组合了AbstractFactory,而工厂方法模式使用了继承。
public class AbstractProductA {
}
public class AbstractProductB {
}
public class ProductA1 extends AbstractProductA {
}
public class ProductA2 extends AbstractProductA {
}
public class ProductB1 extends AbstractProductB {
}
public class ProductB2 extends AbstractProductB {
}
public abstract class AbstractFactory {
abstract AbstractProductA createProductA();
abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA();
}
AbstractProductB createProductB() {
return new ProductB();
}
}
public class ConcreteFactory2 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA();
}
AbstractProductB createProductB() {
return new ProductB();
}
}
public class Client {
public static void main(String[] args) {
AbstractFactory af = new ConcreateFactory1();
AbstractProductA a = af.createProductA();
AbstractProductB b = af.createProductB();
}
}
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
以下是简易的StringBuilder实现,参考了JDK8源码
public class AbstractStringBuilder {
protected char[] value;
protected int count;
public AbstractStringBuilder(int capacity) {
count = 0;
value = new char[capacity];
}
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0) {
expandCapacity(minimumCapacity);
}
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0) {
newCapacity = minimumCapacity;
}
if (newCapacity < 0) {
if (minimumCapacity < 0) {
throw new OutOfMemoryError();
}
newCapacity = Integer.MAX_VALUE;
}
}
}
public class StringBuilder extends AbstractStringBuilder {
public StringBuilder() {
super(16);
}
@Override
public String toString() {
return new String(value, 0, count);
}
}
public class Client {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
final int count = 20;
for (int i = 0; i < count; i++) {
sb.append((char)('a'+i));
}
System.out.println(sb.toString());
}
}
原型模式主要用于对象的复制,它的核心就是类图中的原型类
Prototype
。Prototype
类需要具备以下两个条件:
实现Cloneable
接口。在java
语言有一个Cloneable
接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone
方法。在java
虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException
异常。
重写Object
类的clone
方法。java
中,所有类的父类都是Object
类,Object
类有一个clone
方法,作用是返回对象的一个拷贝,但是其作用域protected
类型的,一般的类无法调用,因此,Prototype
类需要将clone
方法的作用域修改为public
类型。
优点
使用场景
public class PrototypeClass implements Cloneable{
// 覆写父类 Object 方法
@Override
public PrototypeClass clone() {
PrototypeClass prototypeClass = null;
try {
prototypeClass = (PrototypeClass)super.clone();
} catch (CloneNotSupportedException e) {
//异常处理
}
return prototypeClass;
}
}
责任链模式结构的重要核心模块
Handler 抽象处理者
ConcreteHandler 具体处理者
抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者,在具体处理者中可以访问链中下一个对象,以便请求的转发。
public abstract class Handler {
private Handler nextHandler;
//每个处理者都必须对请求做出处理
public final Response handleMessage(Request request) {
Response response = null;
//判断是否是自己的处理级别
if(this.getHandlerLevel().equals(request.getRequestLevel())) {
response = this.echo(request);
} else {
//不属于自己的处理级别
//判断是否有下一个处理者
if(this.nextHandler != null) {
response = this.nextHandler.handleMessage(request);
} else {
//没有适当的处理者,业务自行处理
}
}
return response;
}
// 设置下一个处理者是谁
public void setNext(Handler _handler) {
this.nextHandler = _handler;
}
// 每个处理者都有一个处理级别
protected abstract Level getHandlerLevel();
// 每个处理者都必须实现处理任务
protected abstract Response echo(Request request);
}
下面说一下关键的一些类
execute
方法用来执行命令Command
类的实现类,对抽象类中声明的方法进行实现
public interface Command {
void execute();
}
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
public class LightOffCommand implements Command {
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute(){
light.off();
}
}
public class Light {
public void on() {
System.out.println("light is on");
}
public void off() {
System.out.println("light is off");
}
}
public class Invoker {
private Command[] onCommands;
private Command[] offCommands;;
private final int slotNum = 7;
public Invoker() {
this.onCommands = new Command[slotNum];
this.offCommands = new Command[slotNum];
}
public void setOnCommand(Command command, int slot) {
onCommands[slot] = command;
}
public void setOffCommand(Command command, int slot) {
offCommands[slot] = command;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
}
}
public class Client {
public static void main(String[] args) {
Invoker invoker = new Invoker();
Light light = new Light();
Command lightOnCommand = new LightOnCommand(light);
Command lightOffCommand = new LightOffCommand(light);
invoker.setOnCommand(lightOnCommand, 0);
invoker.setOffCommand(lightOffCommand, 0);
invoker.onButtonWasPushed(0);
invoker.offButtonWasPushed(0);
}
}
TerminalExpression
:终结符表达式,每个终结符都需要一个TerminalExpression
Context
:上下文,包含解释器之外的一些全局信息以下是一个规则检验器实现,具有and和or规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。
public abstract class Expression {
public abstract boolean interpret(String str);
}
public class TerminalExpression extends Expression {
private String literal = null;
public TerminalExpression(String str) {
literal = str;
}
public boolean interpret(String str) {
StringTokenizer st = new StringTokenizer(str);
while(st.hasMoreTokens) {
String test = st.nextToken();
if (test.equals(literal)){
return true;
}
}
return false;
}
}
public class AndExpression extends Expression {
private Expression expression1 = null;
private Expression expression2 = null;
public AndExpression(Expression expression1, Expression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
public boolean interpret(String str){
return expression1.interpret(str) && expression2.interpret(str);
}
}
public class OnExpression extends Expression {
private Expression expression1 = null;
private Expression expression2 = null;
public OrExpression(Expression expression1, Expression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
public boolean interpret(String str) {
return expression1.interpret(str) || expression2.interpret(str);
}
}
public class Client {
public static Expression buildInterpreterTree() {
Expression e1 = new TerminalExpression("A");
Expression e2 = new TerminalExpression("B");
Expression e3 = new TerminalExpression("C");
Expression e4 = new TerminalExpression("D");
Expression a1 = new OrExpression(e2, e3);
Expression a2 = new OrExpression(e1, a1);
return new AndExpression(e4, a2);
}
}
Aggregate
是聚合类,其中createIterator()
方法可以产生一个Iterator
Iterator
主要定义了hasNext()
和next()
方法Client
组合了Aggregate
,为了迭代遍历Aggregate
,也需要组合Iterator
public interface Aggregate {
Iterator createIterator();
}
public class ConcreteAggregate implements Aggregate {
private Integer[] items;
public ConcreteAggregate() {
items = new Integer[10];
for (int i = 0; i < items.length; i++) {
items[i] = i;
}
}
@Override
public Iterator createIterator() {
return new ConcreteIterator<Integer>(items);
}
}
public interface Iterator<Item> {
Item next();
boolean hasNext();
}
public class ConcreteIterator<Item> implements Iterator {
private Item[] items;
private int position = 0;
public ConcreteIterator(Item[] items) {
this.items = items;
}
@Override
public Object next() {
return items[position++];
}
@Override
public boolean hasNext() {
return position < items.length;
}
}
public class Client {
public static void main(String[] args) {
Aggregate aggregate = new ConcreteAggregate();
Iterator<Integer> it = aggregate.createIterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
Mediator
:抽象中介者,定义统一接口用于与各同事(Colleague
)对象通信Concrete Mediator
:具体中介者,通过协调各同事角色实现协作行为,因此它必须依赖于各个同事角色Colleague
:同事,每个同事角色都知道中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。每个同事类地行为分为两种:
一般来说,
同事类
之间的关系是比较复杂的,多个同事类
之间互相关联时,他们之间的关系会呈现为复杂的网状结构
,这是一种过度耦合的架构,不利于类的复用,也不稳定。如果引入中介者模式
,那么同事类
之间的关系将变为星型结构
,任何一个类的变动,只会影响本身
以及中介者
,这样就减小系统的耦合。一个好的设计,必定不会把所有的对象关系处理逻辑封装在本类中,而是使用一个专门的类来管理那些不属于自己的行为。
public abstract class Mediator {
//定义同事类
protected ConcreteColleague1 c1;
protected ConcreteColleague2 c2;
//通过 getter/setter 方法把同事类注入进来
public ConcreteColleague1 getC1() {
return c1;
}
public void setC1(ConcreteColleague1 c1) {
this.c1 = c1;
}
public ConcreteColleague2 getC2() {
return c2;
}
public void setC2(ConcreteColleague2 c2)
{
this.c2 = c2;
}
//中介者模式的业务逻辑
public abstract void doSomething1();
public abstract void doSomething2();
}
以下实现了一个简单计算器程序,可以输入两个值,然后计算两个值的和,备忘录模式允许将这两个值存储起来,然后在某个时刻用储存的状态进行恢复。
public interface Calculator {
PreviousCalculationToCareTaker backupLastCalculation();
void restorePreviousCalculatoin(PreviousCalculationToCareTaker memento);
int getCalculationResult();
void setFirstNumber(int firstNumber);
void setSecondNumber(int secondNumber);
}
public class CalcuatorImp implemnets Calculator {
private int firstNumber;
private int secondNumber;
@Override
public PreviousCalculationToCareTaker backupLastCalculation() {
return new PreviousCalculationImp(firstNumber, secondNumber);
}
@Override
public void restorePreviousCalculation(PreviousCalculationToCareTaker memento) {
this.firstNumber = ((PreviousCalculationToOriginator)memento).getFirstNumber();
this.secondNumber = ((PreviousCalculationToOriginator)memento).getSecondNumber();
}
}
public interface PreviousCalculationToCareTaker {
}
public class PreviousCalculationImp implements PreviousCalculationToCareTaker, PreviousCalculationToOriginator {
private int firstNumber;
private int secondNumber;
public PreviousCalculationImp(int firstNumber, int secondNumber) {
this.firstNumber = firstNumber;
this.secondNumber = secondNumber;
}
@Override
public int getFirstNumber() {
return firstNumber;
}
@Override
public int getSecondNumber() {
return secondNumber;
}
}
public class Client {
public static void main() {
Calculator calculator = new Calculator();
calculator.setFirstNumber(10);
calculator.setSecondNumber(100);
System.out.println(calculator.getCalculationResult());
PreviousCalculationToCareTaker memento = calculator.backupLastCalculation();
calculator.setFirstNumber(17);
calculator.setSecondNumber(-290);
System.out.println(calculator.getCalculationResult());
calculator.restorePreviousCalculation(memento);
System.out.println(calculator.getCalculationResult());
}
}
registerObserver
()方法。
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObserver(Observer o);
}
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeacherData() {
observers = new ArrayList();
}
public void setMeasurements() {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObserver();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
@Override
public void notifyObserver() {
for (Observer o : observers) {
o.update(temperature, humidity, pressure);
}
}
}
public interface Observer {
void update(float temp, float humidity, float pressure);
}
public class StatisticsDisplay implements Observer {
public StatisticsDisplay(Subject weacherData) {
weacherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
System.out.println("StatisticsDisplay.update: "+temp+" "+humidity+" "+pressure);
}
}
public class CurrentConditionsDisplay implements Observer {
public CurrentConditionsDisplay(Subject weacherData) {
weacherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
System.out.println("CurrentConditionsDisplay.update: "+temp+" "+humidity+" "+pressure);
}
}
public class WeacherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay();
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
weatherData.setMeasurements(0, 0, 0);
weatherData.setMeasurements(1, 1, 1);
}
}
使用场景
注意
糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。
public interface State {
void insertQuarter();
void ejectQuarter();
void turnCrank();
void dispense();
}
public class GumballMachine {
private State soldOutState;
private State noQuarterState;
private State hasQuarterState;
private State soldState;
private State state;
private int count = 0;
public GumballMachine(int numberGumballs) {
count = numberGumballs;
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
if (numberGumballs > 0) {
state = noQuarterState;
} else {
state = soldQuarterState;
}
}
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
}
public void setState(State state) {
this.state = state;
}
public void releaseBall() {
System.out.println("A gumball comes rolling out the slot ...");
if (count != 0) {
count -= 1;
}
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarqterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
public int getCount() {
return count;
}
}
public class HasQuarterState implements State {
private GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("You can't insert another quarter");
}
@Override
public void ejectQuarter() {
System.out.println("Quarter returned");
gumballsMachine.setState(gumballMachine.getNoQuarterState());
}
@Override
public void turnCrank() {
System.out.println("You turned ...");
gumballMachine.setState(gumballMachine.getSoldState());
}
@Override
public void dispense() {
System.out.println("No gumball dispensed");
}
}
public class NoQuarterState implements State {
// ...
}
public class SoldOutState implements State {
// ...
}
public class SoldState implements State {
// ...
}
public class Client {
GumballMachine gumballMachine = new GumballMachine(5);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
}
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变Context所组合的State对象,而策略模式是通过Context本身的决策来改变组合的Strategy对象。所谓的状态转移,是指Context在运行过程中由于一些条件发生改变而使得State对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么Context对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换Context使用的算法。
设计一个鸭子,它可以动态地改变叫声,这里的算法族是鸭子的叫声行为。
public interface QuackBehavior {
void quack();
}
public class Quack implements QuackBahavior {
@Override
public void quack() {
System.out.println("quack!");
}
}
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("squeak!");
}
}
public class Duck {
private QuackBehavior quackBehavior;
public void performQuack() {
if (quackBehavior != null) {
quackBehavior.quack();
}
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
public class Client {
public static void main(String[] args) {
Duck duck = new Duck();
duck.setQuackBehavior(new Squeak());
duck.performQuack();
duck.setQuackBehavior(new Quack());
duck.performQuack();
}
}
方法分为两类
使用场景
冲咖啡和冲茶都有类似的流程,但是因为某些步骤会有点不一样,要求复用那些相同步骤的代码。
public abstract class CaffeineBeverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("boilWater");
}
void pourInCup() {
System.out.println("pourInCup");
}
}
public interface Element {
void accept(Visitor visitor);
}
class CustomerGroup {
private List<Customer> customers = new ArrayList();
void accept(Visitor visitor) {
for (Customer customer : customers) {
customer.accept(visitor);
}
}
void addCustomer(Customer customer) {
customers.add(customer);
}
}
public class Customer implements Element {
private String name;
private List<Order> orders = new ArrayList();
Customer(String name) {
this.name = name;
}
String getName() {
return name;
}
void addOrder(Order order) {
orders.add(order);
}
public void accept(Visitor visitor) {
visitor.visit(this);
for (Order order : orders) {
order.accept(visitor);
}
}
}
public class Order implements Element {
private String name;
private List<Item> items = new ArrayList();
Order(String name) {
this.name = name;
}
Order(String name, String itemName) {
this.name = name;
this.addItem(new Item(itemName));
}
String getName() {
return name;
}
void addItem(Item item) {
items.add(item);
}
public void accept(Visitor visitor) {
visitor.visit(this);
for (Item item : items) {
item.accept(visitor);
}
}
}
public class Item implements Element {
private String name;
Item(String name) {
this.name = name;
}
String getName() {
return name;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public interface Visitor {
void visit(Customer customer);
void visit(Order order);
void visit(Item item);
}
public class GeneralReport implements Visitor {
private int customersNo;
private int ordersNo;
private int itemsNo;
public void visit(Customer customer) {
System.out.println(customer.getName());
customersNo++;
}
public void visit(Order order) {
System.out.println(order.getName());
ordersNo++;
}
public void visit(Item item) {
System.out.println(item.getName());
itemsNo++;
}
public void displayResults() {
System.out.println(customersNo);
System.out.println(itemsNo);
System.out.println(ordersNo);
}
}
public class Client {
public static void main(String[] args) {
Customer customer1 = new Customer("customer1");
customer1.addOrder(new Order("order1", "item1"));
customer1.addOrder(new Order("order2", "item1"));
customer1.addOrder(new Order("order3", "item1"));
Order order = new Order("order_a");
order.addItem(new Item("item_a1"));
order.addItem(new Item("item_a2"));
order.addItem(new Item("item_a3"));
Customer customer2 = new Customer("customer2");
customer2.addOrder(order);
CustomerGroup customers = new CustomerGroup();
customers.addCustomer(customer1);
customers.addCustomer(customer2);
GeneralReport visitor = new GeneralReport();
customers.accept(visitor);
visitor.displayResults();
}
}
类适配器
对象适配器
使用场景
鸭子和火鸡拥有两种不同的叫声,鸭子的叫声调用quack方法,而火鸡调用gobble方法。要求将火鸡的gobble方法适配成鸭子的quack方法,从而让火鸡冒充鸭子
public interface Duck {
void quack();
}
public interface Turkey {
void gobble();
}
public class WildTurkey implements Turkey {
@Override
public void gobble() {
System.out.println("gobble!");
}
}
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
@Override
public void quack() {
turkey.gobble();
}
}
public class Client {
public static void main(String[] args) {
Turkey turkey = new WildTurkey();
Duck duck= new TurkeyAdapter(turkey);
duck.quack();
}
}
Abstraction
: 定义抽象类的接口Implementor
: 定义实现类的接口RemoteControl表示遥控器,指代Abstraction。TV表示电视,指代Implementor。桥接模式将遥控器和电视分离开来,从而可以独立改变遥控器或者电视的实现。
public abstract class TV {
public abstract void on();
public abstract void off();
public abstract void tuneChannel();
}
public class Sony extends TV {
@Override
public void on() {
System.out.println("Sony.on()");
}
@Override
public void off() {
System.out.println("Sony.off()");
}
@Override
public void tuneChannel() {
System.out.println("Sony.tuneChannel()");
}
}
public class Composite extends Component {
//构件容器
private ArrayList<Component> componentArrayList = new ArrayList<Component>();
//增加一个叶子构件或树枝构件
public void add(Component component) {
this.componentArrayList.add(component);
}
//删除一个叶子构件或树枝构件
public void remove(Component component) {
this.componentArrayList.remove(component);
}
//获得分支下的所有叶子构件和树枝构件 public
ArrayList<Component> getChildren() {
return this.componentArrayList;
}
}
Component 抽象构件
Concrete Component 具体构件
Decorator 装饰角色
Concrete Decorator 具体装饰角色
使用场景
装饰者(Decorator
)和具体组件(ConcreteComponent
)都继承自组件(Component
)
,具体组件的方法实现不需要依赖于其他对象,而装饰者组合了一个组件,这样它可以装饰其他装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最底层,因为只有具体组件的方法实现不需要依赖于其他对象。
设计原则:类应该对扩展开放,对修改关闭。也就是添加新功能时不需要修改代码。饮料可以动态添加新的配料,而不需要去修改饮料的代码。
设计原则:只和你的密友谈话,也就是说客户对象所需要交互的对象应当尽可能的少。
public class FlyweightFactory {
//定义一个池容器
private static HashMap<String,Flyweight> pool = new HashMap<String,Flyweight>();
//享元工厂
public static Flyweight getFlyweight(String Extrinsic){
//需要返回的对象
Flyweight flyweight = null;
//在池中没有该对象
if( pool.containsKey(Extrinsic) ) {
flyweight = pool.get(Extrinsic);
} else {
//根据外部状态创建享元对象
flyweight = new ConcreteFlyweight1(Extrinsic);
//放置到池中
pool.put(Extrinsic, flyweight);
}
return flyweight;
}
}
修改一个类的原因应该只有一个
换句话说就是让一个类只负责一件事,当这个类需要做过多事情时,就需要分解这个类。如果一个类承担的指责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱这个类完成其他职责的能力。
类应该对扩展开放,对修改关闭
扩展就是添加新功能的意思,因此该原则要求在添加新功能时不需要修改代码。符合开闭原则最典型的设计模式就是装饰者模式
,它可以动态地将责任附加到对象上,而不用去修改类的代码。
子类对象必须能够替换掉所有父类对象
继承是一种IS-A
关系,子类需要能够当成父类来使用,并且需要比父类更特殊。如果不满足这个原则,那么各个子类的行为上就会有很大差异,增加继承体系的复杂度。
不应该强迫客户依赖于它们不用的方法
因此使用多个专门的接口比使用单一的总接口要好
高层模块不应该依赖于底层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。
高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于底层模块,那么底层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。
依赖于抽象意味着:
LKP,最少知识原则,一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
尽量使用对象组合,而不是通过继承来达到复用的目的
一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。
最稳定的包应该是最抽象的包,不稳定的包应该是具体的包,即包的抽象程度跟他的稳定性成正比
包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具有稳定性
的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合
修改一个类的原因应该只有一个
换句话说就是让一个类只负责一件事,当这个类需要做过多事情时,就需要分解这个类。如果一个类承担的指责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱这个类完成其他职责的能力。
类应该对扩展开放,对修改关闭
扩展就是添加新功能的意思,因此该原则要求在添加新功能时不需要修改代码。符合开闭原则最典型的设计模式就是装饰者模式
,它可以动态地将责任附加到对象上,而不用去修改类的代码。
子类对象必须能够替换掉所有父类对象
继承是一种IS-A
关系,子类需要能够当成父类来使用,并且需要比父类更特殊。如果不满足这个原则,那么各个子类的行为上就会有很大差异,增加继承体系的复杂度。
不应该强迫客户依赖于它们不用的方法
因此使用多个专门的接口比使用单一的总接口要好
高层模块不应该依赖于底层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。
高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于底层模块,那么底层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。
依赖于抽象意味着:
LKP,最少知识原则,一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
尽量使用对象组合,而不是通过继承来达到复用的目的
一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。
最稳定的包应该是最抽象的包,不稳定的包应该是具体的包,即包的抽象程度跟他的稳定性成正比
包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具有稳定性