面向对象的六大原则
1.单一职责原则——SRP(Single Responsibility Principle)
一个类中应该是一组相关性很高的函数、数据的封装
2.开闭原则——OCP(Open Close Principle)
软件中的对象(类、模块和函数等)应该对于扩展是开放的,但是对于修改是封闭的
3.里氏替换原则——LSP(Lisdow Substitution Principle)
如果对没一个类型为S的对象O1,都有类型为T的对象O2,使得以T定义的所有程序P在所有的对象O1替换为O2时,程序P的行为没有发生变化,那么类型S就是类型T的子类型
4.依赖倒置原则——DIP(Dependence Inversion Principle)
指代了一种特定的解耦形式,使得高层次的模块不依赖于低层次的模块的实现细节的目的,依赖模块被颠倒了
5.接口隔离原则——ISP(InterfaceSegregation Principles)
客户端不应该依赖它不需要的接口
6.迪米特原则——LOD(Law of Demeter)
一个对象应该对其他对象有最少的了解
创建型模式:5个
单例模式、Builder、原型模式、工厂方法、抽象工厂
行为型模式: 11个
策略模式、状态模式、观察者模式、中介者模式、访问者模式、迭代器模式、模板方法、备忘录模式、命令模式、解释器模式、职责链模式
结构型模式:7个
组合模式、代理模式、装饰模式、外观模式、享元模式、享元模式、桥接模式、适配器模式
1.单例模式
定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
使用场景:
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该且只有一个,例如访问IO和数据库等资源
单例模式的主要关键点:
-
构造函数不对外开放,一般为private
-
通过一个静态方法或者枚举返回单例类对象
-
确保单例类的对象有且只有一个,尤其在多线程环境下
-
确保单例类对象在反序列化时不会重新构建对象
单例模式常用的实现方式
-
饿汉单例——在声明静态对象时就已经初始化
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} } -
懒汉单例——声明静态对象,在用户第一次调用getInstance时进行初始化
public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } -
Double CheckLock(DCL)实现单例——既能在需要时才初始化,又能保证线程安全,并且单例对象初始化后调用getInstance不进行同步锁
public class Singleton { private static Singleton instance = null; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } -
静态内部类单例模式——建议用于解决DCL在某些情况下出现失效的问题
public class Singleton { private Singleton() {} public static Singleton getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } } -
枚举单例
public enum SingletonEnum { INSTANCE; public void doSomething() { System.out.println("do sth"); } } -
使用容器实现单例模式
public class SingletonManager { private static Map<String, Object> objMap = new HashMap<>(); private SingletonManager() {} public static void registerService(String key, Object instance) { if (!objMap.containsKey(key)) { objMap.put(key, instance); } } public static Object getService(String key) { return objMap.get(key); } }
优点
- 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显
- 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式
- 单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问
缺点
- 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现
- 单例对象如果持有Context,很容易内存泄露,传递时最好用Application Context
2.Builder模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
使用场景:
-
相同的方法,不同的执行顺序,产生不同的事件结果时
-
多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时
-
产品类非常复杂,或者产品类中调用顺序不同产生了不同的作用,这个时候使用建造者模式非常适合
-
当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值
优点
- 良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节
- 建造者独立,容易扩展
缺点
- 会产生多余的Builder对象以及Director对象,消耗内存
简单实现
public class Phone {
private String cpu;
private String camera;
private String screen;
private String battery;
public Phone(Builder builder) {
this.cpu = builder.cpu;
this.camera = builder.camera;
this.screen = builder.screen;
this.battery = builder.battery;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getCamera() {
return camera;
}
public void setCamera(String camera) {
this.camera = camera;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public String getBattery() {
return battery;
}
public void setBattery(String battery) {
this.battery = battery;
}
public static class Builder {
private String cpu;
private String camera;
private String screen;
private String battery;
public Builder() {
}
public Builder setCpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder setCamera(String camera) {
this.camera = camera;
return this;
}
public Builder setScreen(String screen) {
this.screen = screen;
return this;
}
public Builder setBattery(String battery) {
this.battery = battery;
return this;
}
public Phone build() {
return new Phone(this);
}
}
}
3.原型模式
定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象
使用场景:
- 类初始化需要消耗 非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗
- 通过new产生一个对象需要非常频繁的数据准备或者访问权限,这时可以使用原型模式
- 一个对象需要提供给其他对象访问,而且各个调用者都可能需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
优点
原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量对象时,原型模式可以更好地体现其有点
缺点
- 直接在内存中拷贝,构造函数不会执行,在实际应用开发中需要注意这个潜在的问题
- 使用浅拷贝操作副本时可能会影响原始对象
简单实现
public class WordDocument implements Cloneable {
private String text;
private ArrayList<String> imgs = new ArrayList<>();
public WordDocument() {
System.out.println("----------构造函数----------");
}
@Override
protected Object clone() throws CloneNotSupportedException {
try {
WordDocument doc = (WordDocument) super.clone();
doc.text = this.text;
//1.浅拷贝
doc.imgs = this.imgs;
//2.深拷贝--对imgs对象也调用clone()函数,进行深拷贝,正常建议使用深拷贝
doc.imgs = (ArrayList<String>) this.imgs.clone();
return doc;
} catch (Exception ignored) {
}
return null;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public ArrayList<String> getImgs() {
return imgs;
}
public void setImgs(ArrayList<String> imgs) {
this.imgs = imgs;
}
}
4.工厂方法模式
定义:定义一个用于创建对象的接口,让子类决定实例化哪个类
使用场景:
在任何需要生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式,用new就可以完成创建的对象无需使用工厂模式
简单实现:
public abstract class BaseFactory {
public abstract void showInfo();
}
public class A extends BaseFactory {
@Override
public void showInfo() {
System.out.println("这是A");
}
}
public class B extends BaseFactory {
@Override
public void showInfo() {
System.out.println("这是B");
}
}
5.抽象工厂模式
定义:为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定它们的具体类
使用场景:
一个对象族有相同的约束时可以使用抽象工厂模式
优点
分离接口与实现,客户端使用抽象工厂来创建需要的对象,而客户端根本不知道具体的实现是谁,客户端只是面向产品的接口编程,使其从具体的产品实现中解耦,同时基于接口与实现的分离,使抽象工厂方法模式在切换产品类时更加灵活、容易
缺点
- 对类文件的爆炸性增加
- 不太容易扩展新的产品类,因为每当我们增加一个产品类就需要修改抽象工厂,那么所有的具体工厂类都会被修改
简单实现
//抽象产品A接口
public interface IProductA {
void method();
}
//具体产品类A1和A2
public class ProductA1 implements IProductA {
@Override
public void method() {
System.out.println("产品A1");
}
}
public class ProductA2 implements IProductA {
@Override
public void method() {
System.out.println("产品A2");
}
}
//抽象产品B接口
public interface IProductB {
void method();
}
//具体产品类B1和B2
public class ProductB1 implements IProductB {
@Override
public void method() {
System.out.println("产品B1");
}
}
public class ProductB2 implements IProductB {
@Override
public void method() {
System.out.println("产品B2");
}
}
//抽象工厂类
public abstract class AbstractFactory {
public abstract IProductA getProductA();
public abstract IProductB getProductB();
}
//具体工厂类A和具体工厂类B
public class ConcreteFactoryA extends AbstractFactory {
@Override
public IProductA getProductA() {
return new ProductA1();
}
@Override
public IProductB getProductB() {
return new ProductB2();
}
}
public class ConcreteFactoryB extends AbstractFactory {
@Override
public IProductA getProductA() {
return new ProductA2();
}
@Override
public IProductB getProductB() {
return new ProductB1();
}
}
6.策略模式
定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化
使用场景:
- 针对同一类型问题的多重处理方式,仅仅是具体行为有差别时
- 需要安全地封装多种同一类型的操作时
- 出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时
优点
- 结构清晰明了、使用简单直观
- 耦合度 相对而言较低,扩展方便
- 操作封装也更为彻底,数据更为安全
缺点
随着策略的增加,子类也会变得繁多
简单实现
//创建一个接口
public interface Strategy {
public int doOperation(int num1, int num2);
}
//创建实现接口的实体类
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubstract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
//创建Context类
public class Context {
private Strategy strategy;
public Context(){}
public SetStrategy(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
//使用 Context 来查看当它改变策略 Strategy 时的行为变化。
public static void main(String[] args) {
Context context = new Context();
context.SetStrategy(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context.SetStrategy(new OperationSubstract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context.SetStrategy(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
7.状态模式
定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类
使用场景
- 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为
- 代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有庞大的多分支语句(if-else或switch-case),且这些分支依赖于该对象的状态
优点
State模式将所有与一个特定的状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将烦琐的状态结构判断转换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性和可维护性
缺点
状态模式的使用必然会增加系统类和对象的个数
简单实现
//创建一个接口
public interface State {
public void start(Context context);
public void close(Context context);
}
//创建实现接口的实体类
public class StartState implements State {
public void start(Context context) {
}
public void close(Context context) {
context.setState(new CloseState());//注意状态的切换
System.out.println("close State");
}
}
public class CloseState implements State {
public void start(Context context) {
context.setState(new StartState());//注意状态的切换
System.out.println("start State");
}
public void close(Context context) {
}
}
//创建 Context 类
public class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void start() {
getState().start(this);
}
public void close() {
getState().close(this);
}
}
//使用 Context 来查看当状态 State 改变时的行为变化
public class StatePatternDemo {
public static void main(String... args) {
Context context = new Context();
// 初始为开始状态
context.setState(new StartState());
// 切换为关闭状态
context.close();
// 切换为开始状态
context.start();
}
}
8.责任链模式
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止
使用场景
- 多个对象可以处理同一请求,但具体由哪个对象处理则在行动时动态决定
- 在请求处理者不明确的情况下向多个对象中的一个 提交一个请求
- 需要动态指定一组对象处理请求
优点
可以对请求者和处理者关系解耦,提高代码的灵活性
缺点
对链中请求处理者的遍历,如果处理者太多那么遍历必定会影响性能,特别是在一些递归调用中
简单实现
//创建抽象的请求者
public abstract class AbstractRequest {
private Object obj;
public AbstractRequest(Object obj) {
this.obj = obj;
}
public Object getContent() {
return obj;
}
public abstract int getRequestLevel();
}
//创建请求者实体类
public class Request1 extends AbstractRequest{
public Request1(Object obj) {
super(obj);
}
@Override
public int getRequestLevel() {
return 1;
}
}
public class Request2 extends AbstractRequest{
public Request2(Object obj) {
super(obj);
}
@Override
public int getRequestLevel() {
return 2;
}
}
public class Request3 extends AbstractRequest{
public Request3(Object obj) {
super(obj);
}
@Override
public int getRequestLevel() {
return 3;
}
}
//创建抽象的处理者
public abstract class AbstractHandler {
protected AbstractHandler nextHandler;
public final void handleRequest(AbstractRequest request) {
if (getHandleLevel() == request.getRequestLevel()) {
handle(request);
} else {
if (nextHandler != null) {
nextHandler.handle(request);
} else {
System.out.println("无法处理请求");
}
}
}
protected abstract int getHandleLevel();
protected abstract void handle(AbstractRequest request);
}
//创建处理者实体类
public class Handler1 extends AbstractHandler {
@Override
protected int getHandleLevel() {
return 1;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handler1 handle request:" + request.getRequestLevel());
}
}
public class Handler2 extends AbstractHandler {
@Override
protected int getHandleLevel() {
return 2;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handler2 handle request:" + request.getRequestLevel());
}
}
public class Handler3 extends AbstractHandler {
@Override
protected int getHandleLevel() {
return 3;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handler3 handle request:" + request.getRequestLevel());
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
AbstractHandler handler1 = new Handler1();
AbstractHandler handler2 = new Handler2();
AbstractHandler handler3 = new Handler3();
//形成责任链
handler1.nextHandler = handler2;
handler2.nextHandler = handler3;
AbstractRequest request1 = new Request1("Request1");
AbstractRequest request2 = new Request1("Request2");
AbstractRequest request3 = new Request1("Request3");
//正常从链式的首端发起请求
handler1.handle(request1);
handler1.handle(request2);
handler1.handle(request3);
}
}
9.解释器模式
定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来结算语言中的句子
使用场景
- 如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象语法树时可以考虑使用
- 在某些特定领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则如下的语句,然后构建解释器来解释该语句
优点
具有灵活的扩展性,当我们相对文法规则进行扩展延伸时,只需要增加相应的非终结符解释器,并在构建抽象语法树时,使用到新增的解释器对象进行具体的解释即可
缺点
- 对于每一条文法都可以对应至少一个解释器,会生产大量的类,导致维护困难
- 对于过于复杂的文法,构建其抽象语法树会显得异常繁琐,甚至可能会出现需要构建多棵抽象语法树的情况,因此,对于复杂的文法并不推荐使用解释器模式
简单实现
//创建一个接口表达式
public interface Expression {
public boolean interpret(String context);
}
//创建实现了上述接口的实体类
public class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data){
this.data = data;
}
@Override
public boolean interpret(String context) {
if(context.contains(data)){
return true;
}
return false;
}
}
public class OrExpression implements Expression {
private Expression expr1 = null;
private Expression expr2 = null;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
public class AndExpression implements Expression {
private Expression expr1 = null;
private Expression expr2 = null;
public AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) && expr2.interpret(context);
}
}
//InterpreterPatternDemo 使用 Expression 类来创建规则,并解析它们
public class InterpreterPatternDemo {
//规则:Robert 和 John 是男性
public static Expression getMaleExpression(){
Expression robert = new TerminalExpression("Robert");
Expression john = new TerminalExpression("John");
return new OrExpression(robert, john);
}
//规则:Julie 是一个已婚的女性
public static Expression getMarriedWomanExpression(){
Expression julie = new TerminalExpression("Julie");
Expression married = new TerminalExpression("Married");
return new AndExpression(julie, married);
}
public static void main(String[] args) {
Expression isMale = getMaleExpression();
Expression isMarriedWoman = getMarriedWomanExpression();
System.out.println("John is male? " + isMale.interpret("John"));
System.out.println("Julie is a married women? "
+ isMarriedWoman.interpret("Married Julie"));
}
}
10.命令模式
定义:将一个请求封装为一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作
使用场景
- 某一对象有一系列的事物操作
- 具有抽象行为的动作,支持多种类型的操作
优点
更弱的耦合性、更灵活的控制性以及更好的扩展性
缺点
容易有类的膨胀,大量衍生类的创建,简单的行为命令反而复杂
简单实现
//创建一个命令接口
public interface Order {
void execute();
}
//创建一个请求类
public class Stock {
private String name = "ABC";
private int quantity = 10;
public void buy(){
System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] buy");
}
public void sell(){
System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] sell");
}
}
//创建实现了 Order 接口的实体类
public class BuyStock implements Order {
private Stock abcStock;
public BuyStock(Stock abcStock){
this.abcStock = abcStock;
}
public void execute() {
abcStock.buy();
}
}
public class SellStock implements Order {
private Stock abcStock;
public SellStock(Stock abcStock){
this.abcStock = abcStock;
}
public void execute() {
abcStock.sell();
}
}
//创建命令调用类
public class Broker {
private List<Order> orderList = new ArrayList<Order>();
public void takeOrder(Order order){
orderList.add(order);
}
public void placeOrders(){
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
//使用 Broker 类来接受并执行命令
public class CommandPatternDemo {
public static void main(String[] args) {
Stock abcStock = new Stock();
BuyStock buyStockOrder = new BuyStock(abcStock);
SellStock sellStockOrder = new SellStock(abcStock);
Broker broker = new Broker();
broker.takeOrder(buyStockOrder);
broker.takeOrder(sellStockOrder);
broker.placeOrders();
}
}
11.观察者模式
定义:定义对象见一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并更新
使用场景:
- 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系
- 事件多级触发场景
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制
优点
- 观察者和被观察者之间是抽象耦合,应对业务变化
- 增强系统灵活性、可扩展性,建立一套触发机制
缺点
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化
简单实现
//观察者
public class Coder implements Observer {
private String name;
public Coder(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println("Hi," + name + ",网站更新内容了:" + arg);
}
}
//被观察者
public class Website extends Observable {
public void callCoder(String content) {
setChanged();
notifyObservers(content);
}
}
//测试调用
public class Test {
public static void main(String[] args) {
Website website = new Website();
Coder xiaoMing = new Coder("小明");
Coder xiaoHong = new Coder("小红");
Coder xiaoFang = new Coder("小芳");
website.callCoder("出了最新的技术了");
}
}
12.备忘录模式
定义:在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可以将该对象恢复到原先保存的状态
使用场景
- 需要保存一个对象在某一个时刻的状态或部分状态
- 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态
优点
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
- 实现了信息的封装,使得用户不需要关心状态的保存细节
简单实现
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
13.迭代器模式
定义:提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示
使用场景
遍历一个容器对象时
优点
- 它支持以不同的方式遍历一个聚合对象。
- 迭代器简化了聚合类。
- 在同一个聚合上可以有多个遍历。
- 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
简单实现
//迭代器接口
public interface Iterator {
boolean hasNext();
Object next();
}
//具体迭代器
public class ConcreteIterator implements Iterator {
private int position;
private List list = new ArrayList();
public ConcreteIterator(List list){
this.list = list;
}
@Override
public boolean hasNext() {
if (position == list.size()) {
return false;
} else {
return true;
}
}
@Override
public Object next() {
Object obj = null;
if(this.hasNext()){
obj = this.list.get(position++);
}
return obj;
}
}
//容器接口
public interface Aggregate {
void add(Object obj);
void remove(Object obj);
Iterator iterator();
}
//具体容器
public class ConcreteAggregate implements Aggregate {
private List list = new ArrayList();
public void add(Object obj) {
list.add(obj);
}
public Iterator iterator() {
return new ConcreteIterator(list);
}
public void remove(Object obj) {
list.remove(obj);
}
}
//测试调用
public class Test {
public static void main(String[] args) {
Aggregate aggregate = new ConcreteAggregate();
aggregate.add("1");
aggregate.add("2");
aggregate.add("3");
aggregate.add("4");
aggregate.add("5");
aggregate.add("6");
Iterator iterator = aggregate.iterator();
while (iterator.hasNext()) {
System.out.println("\n" + iterator.next());
}
}
}
14.模板方法模式
定义:定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构可重定义该算法的某些特定步骤
使用场景
- 多个子类有公有方法,并且逻辑基本相同时
- 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能由各个子类实现
- 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类,然后通过钩子函数约束其行为
优点
- 封装不变部分,扩展可变部分
- 提取公共部分代码,便于维护
缺点
- 模板方法会带来代码阅读的难度,会让用户觉得难以理解
简单实现
//创建一个抽象类,它的模板方法被设置为 final。
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//模板
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
//创建扩展了上述类的实体类。
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
//使用 Game 的模板方法 play() 来演示游戏的定义方式。
public class TemplatePatternDemo {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}
15.访问者模式
定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作
使用场景
- 对象结构比较稳定,但经常需要在此对象结构上定义新的操作
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望再增加新操作时修改这些类
优点
- 各角色职责分离,符合单一职责原则
- 具有优秀的扩展性
- 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化
- 灵活性
缺点
- 具体原生对访问者公布细节,违反了迪米特原则
- 具体元素变更时导致修改成本大
- 违反了依赖倒置原则,为了达到“区别对待”而依赖了具体类,没有依赖抽象
简单实现
//定义一个表示元素的接口
public interface ComputerPart {
public void accept(ComputerPartVisitor computerPartVisitor);
}
//创建扩展了上述类的实体类。
public class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
public class Monitor implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
public class Mouse implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
public class Computer implements ComputerPart {
ComputerPart[] parts;
public Computer(){
parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
}
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
//定义一个访问者接口
public interface ComputerPartVisitor {
public void visit(Computer computer);
public void visit(Mouse mouse);
public void visit(Keyboard keyboard);
public void visit(Monitor monitor);
}
//创建实现了上述类的实体访问者。
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("Displaying Computer.");
}
@Override
public void visit(Mouse mouse) {
System.out.println("Displaying Mouse.");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying Keyboard.");
}
@Override
public void visit(Monitor monitor) {
System.out.println("Displaying Monitor.");
}
}
//使用 ComputerPartDisplayVisitor 来显示 Computer 的组成部分。
public class VisitorPatternDemo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
}
}
16.中介者模式
定义:中介者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使它们可以松散耦合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。中介者模式将多对多的相互作用转化为一对多的相互作用。中介者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理
使用场景
多个类相互耦合,形成了网状结构。
优点
- 如果类的依赖关系如网状般错综复杂,适当使用中介者模式可以对这种依赖关系进行解耦使逻辑结构清晰
缺点
- 如果类之间的依赖关系不复杂,使用中介者反而会使得原本的逻辑结构变得复杂
简单实现
//创建User类
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String name) {
this.name = name;
}
public void sendMessage(String message) {
ChatRoom.showMessage(this, message);
}
}
//创建中介类
public class ChatRoom {
public static void showMessage(User user, String message){
System.out.println(new Date().toString()
+ " [" + user.getName() +"] : " + message);
}
}
//使用 User 对象来显示他们之间的通信。
public class MediatorPatternDemo {
public static void main(String[] args) {
User robert = new User("Robert");
User john = new User("John");
robert.sendMessage("Hi! John!");
john.sendMessage("Hello! Robert!");
}
}
17.代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问
使用场景
当无法或不想直接访问某个对象或者访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口
优点
- 职责清晰。
- 高扩展性。
- 智能化。
缺点
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
简单实现
//创建一个接口。
public interface Image {
void display();
}
//创建实现接口的实体类。
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
//当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
}
18.组合模式
定义:将对象组合成树形结构以表示是‘’部分-整体“的层次结构,使得用户对单个对象和组合对象的使用具有一致性
使用场景
- 表示对象的部分-整体层次结构时
- 从一个整体中能独立出部分模块或功能的场景
优点
- 高层模块调用简单。
- 节点自由增加。
简单实现
//创建 Employee 类,该类带有 Employee 对象的列表
public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates;
//构造函数
public Employee(String name,String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates(){
return subordinates;
}
public String toString(){
return ("Employee :[ Name : "+ name
+", dept : "+ dept + ", salary :"
+ salary+" ]");
}
}
//使用 Employee 类来创建和打印员工的层次结构
public class CompositePatternDemo {
public static void main(String[] args) {
Employee CEO = new Employee("John","CEO", 30000);
Employee headSales = new Employee("Robert","Head Sales", 20000);
Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
Employee clerk1 = new Employee("Laura","Marketing", 10000);
Employee clerk2 = new Employee("Bob","Marketing", 10000);
Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//打印该组织的所有员工
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
}
}
19.适配器模式
定义:适配器模式把一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
使用场景
- 系统需要使用现有的类,而此类接口不符合系统的需要,即接口不兼容
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作
- 需要一个统一的输出接口,而输入端的类型不可预知
优点
-
更好的复用性
系统需要使用现有的类,而此类的接口不符合系统的需要。使用适配器模式就可以让这些功能得到更好的复用
-
更好的扩展性
在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能
缺点
- 过多使用适配器会让系统非常凌乱,不容易整体把握
- 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
简单实现
//为媒体播放器和更高级的媒体播放器创建接口。
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
//创建实现了 AdvancedMediaPlayer 接口的实体类。
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
@Override
public void playMp4(String fileName) {
//什么也不做
}
}
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
//什么也不做
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}
//创建实现了 MediaPlayer 接口的适配器类。
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
//创建实现了 MediaPlayer 接口的实体类。
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
//播放 mp3 音乐文件的内置支持
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: "+ fileName);
}
//mediaAdapter 提供了播放其他文件格式的支持
else if(audioType.equalsIgnoreCase("vlc")
|| audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media. "+
audioType + " format not supported");
}
}
}
//使用 AudioPlayer 来播放不同类型的音频格式。
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
20.装饰模式
定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活
使用场景
需要透明且动态地扩展类的功能时
优点
- 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点
- 多层装饰比较复杂。
简单使用
//创建一个接口
public interface Shape {
void draw();
}
//创建实现接口的实体类
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
//创建实现了 Shape 接口的抽象装饰类
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
//创建扩展了 ShapeDecorator 类的实体装饰类
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
//使用 RedShapeDecorator 来装饰 Shape 对象
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
//Shape redCircle = new RedShapeDecorator(new Circle());
//Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
21.享元模式
定义:使用共享对象可有效地支持大量的细粒度的对象
使用场景
- 系统中存在大量的相似对象
- 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份
- 需要缓冲池的场景
优点
- 大大减少对象的创建,降低系统的内存,使效率提高。
缺点
- 使得系统更加复杂,为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化
- 将享元对象的状态外部化,而读取外部状态使时间稍微边长
简单使用
//创建一个接口。
public interface Shape {
void draw();
}
//创建实现接口的实体类。
public class Circle implements Shape {
private String color;
private int x;
private int y;
private int radius;
public Circle(String color){
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setRadius(int radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Circle: Draw() [Color : " + color + ", x : " + x +", y :" + y +", radius :" + radius);
}
}
//创建一个工厂,生成基于给定信息的实体类的对象。
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle)circleMap.get(color);
if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color : " + color);
}
return circle;
}
}
//使用该工厂,通过传递颜色信息来获取实体类的对象。
public class FlyweightPatternDemo {
private static final String colors[] =
{ "Red", "Green", "Blue", "White", "Black" };
public static void main(String[] args) {
for(int i=0; i < 20; ++i) {
Circle circle =
(Circle)ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int)(Math.random()*colors.length)];
}
private static int getRandomX() {
return (int)(Math.random()*100 );
}
private static int getRandomY() {
return (int)(Math.random()*100);
}
}
22.外观模式
定义:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,外观模式提供一个高层次的接口,使得子系统更易于使用
使用场景
- 为复杂的模块或子系统提供外界访问的模块。
- 子系统相对独立。
- 预防低水平人员带来的风险。
优点
- 对客户程序隐藏子系统细节,因而减少了客户对于子系统的耦合,能够拥抱变化
- 外观类对子系统的接口封装,使得系统更易于使用
缺点
- 外观类接口膨胀。由于子系统的接口都有外观类统一对外暴露,使得外观类的API接口较多,在一定程度上增加了用户使用成本
- 外观类没有遵循开闭原则,当业务出现变更时,可能需要直接修改外观类
简单实现
/** * 电脑接口 */
public interface Computer {
void open();
}
/** * CPU类 */
class Cpu implements Computer {
@Override
public void open() {
System.out.println("启动CPU");
}
}
/** * 内存类 */
class Ddr implements Computer {
@Override
public void open() {
System.out.println("启动内存");
}
}
/** * 硬盘类 */
class Ssd implements Computer {
@Override
public void open() {
System.out.println("启动硬盘");
}
}
/** * 外观类 */
public class Facade {
private Computer cpu;
private Computer ddr;
private Computer ssd;
/** * 启动cpu */
public void onCPU() {
cpu = new Cpu();
cpu.open();
}
/** * 启动内存 */
public void onDDR() {
ddr = new Ddr();
ddr.open();
}
/** * 启动硬盘 */
public void onSSD() {
ssd = new Ssd();
ssd.open();
}
}
public class FacadeTest34 {
public static void main(String[] args) {
Facade facade = new Facade();
facade.onSSD();
}
}
23.桥接模式
定义:将抽象部分与实现部分分离,使它们都可以独立地进行变化
使用场景
- 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
- 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
- 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
优点
- 抽象和实现的分离。
- 优秀的扩展能力。
- 实现细节对客户透明。
缺点
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
简单实现
//创建桥接实现接口。
public interface DrawAPI {
public void drawCircle(int radius, int x, int y);
}
//创建实现了 DrawAPI 接口的实体桥接实现类。
public class RedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: red, radius: "
+ radius +", x: " +x+", "+ y +"]");
}
}
public class GreenCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: green, radius: "
+ radius +", x: " +x+", "+ y +"]");
}
}
//使用 DrawAPI 接口创建抽象类 Shape。
public abstract class Shape {
protected DrawAPI drawAPI;
protected Shape(DrawAPI drawAPI){
this.drawAPI = drawAPI;
}
public abstract void draw();
}
//创建实现了 Shape 接口的实体类。
public class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
public void draw() {
drawAPI.drawCircle(radius,x,y);
}
}
//使用 Shape 和 DrawAPI 类画出不同颜色的圆。
public class BridgePatternDemo {
public static void main(String[] args) {
Shape redCircle = new Circle(100,100, 10, new RedCircle());
Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
redCircle.draw();
greenCircle.draw();
}
}