JAVA设计模式

面向对象设计的六大设计原则

1、单一职责原则(Single Responsibility Principle, SRP):一个类应该,完整负责且只负责一个领域。完整负责(高内聚)以减少零散的类、只负责(低耦合)以方便被复用
2、接口隔离原则(Interface Segregation Principle, ISP):即接口的单一职责原则
3、迪米特法则(Law of Demeter, LoD):一个类不要直接去访问其对象成员的对象成员,可以通过其对象成员间接访问。使得,修改一个类时,只涉及直接使用它的类,而不涉及间接使用它的类,以降低耦合度
4、开闭原则(Open-Closed Principle, OCP):一个类应该,对扩展开放,对修改关闭。即在不被修改的情况下也能被扩展
5、里氏代换原则(Liskov Substitution Principle, LSP):一个基类声明的变量,必须也能引用其子类的对象,反过来则不行。这是JAVA语言内置特性,即子类对象可以转换为父类,反过来则不行
6、依赖倒转原则(Dependency Inversion Principle, DIP):尽量用抽象类声明变量,具体类中尽量只实现抽象类中有声明的方法。使得,在新增具体类时,不用修改已有的可能引用它的地方,且它的方法都能被访问到

六大设计原则之间的关系:
单一职责原则、接口隔离原则、迪米特法则都是为了低耦合
里氏代换原则是实现开闭原则的基础,依赖倒转原则是实现开闭原则的一个基本手段
解耦的两种形式:单一职责原则(把一个整体拆分成相对独立的模块),迪米特法则

模式分类

创建型模式,5种:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式
结构型模式,7种:适配器模式、桥接模式、组合模式、装饰器模式、外观模式(门面模式)、享元模式、代理模式
行为型模式,11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

优缺点分析

复杂度,代码复用,解耦,开闭原则

1、单例模式(Singleton)

懒汉式

public class Singleton {
    private static Singleton instance=null;
    private Singleton(){    }
    public static Singleton getInstance(){
        if(instance==null){            //这里线程不安全
            instance=new Singleton();
        }
        return instance;
    }
}

饿汉式

public class Singleton {
    private static Singleton instance=new Singleton();  //这里超前加载了
    private Singleton(){    }
    public static Singleton getInstance(){
        return instance;
    }
}

内部类式

public class Singleton {
    private Singleton(){    }
    private static class SingletonHolder{
        private final static Singleton instance=new Singleton();
    }
    //调用getInstance,才会去加载内部类的 static 成员,实现了延迟加载
    public static Singleton getInstance(){    
        return SingletonHolder.instance;
    }
}

理解:只产生一个实例,节约资源,只要上一个对象锁就能保证原子性
JDK案例:Runtime.getRuntime();NumberFormat.getInstance();

2、工厂方法模式

简单工厂模式(静态工厂方法模式,Simple Factory)

public class Factory{
    public static Sample creator(int which){
        if (which==1)
            return new SampleA();
        else if (which==2)
            return new SampleB();
    }
}

理解:一个工厂的同一个方法,生产不同产品(限共同父类)
JDK案例:Integer.valueOf(); Calendar.getInstance();java.lang.Class.forName()

工厂方法模式(Factory Method)

抽象产品,具体产品A,具体产品B,抽象工厂,具体工厂A,具体工厂B
使用:
抽象工厂 factory = new 具体工厂A();
抽象产品 m = factory.create();

理解:不同工厂的同一个方法,生产不同产品(限共同父类)。新增具体产品时,新增工厂,不用去改已有的工厂,符合开闭原则。
JDK案例:1、抽象工厂方法 java.util.Collection.iterator(),抽象产品 java.util.Iterator
2、java.lang.Object#toString()

3、抽象工厂模式(Abstract Factory)

// 具体工厂类,其中Food,Vehicle,Weapon是抽象产品
public class DefaultFactory extends AbstractFactory{ 
    public Food createFood() {
        return new Apple();
    }
    public Vehicle createVehicle() {
        return new Car();
    }
    public Weapon createWeapon() {
        return new AK47();
    }
}
使用:
AbstractFactory f = new DefaultFactory();
Vehicle v = f.createVehicle();
Weapon w = f.createWeapon();
Food food = f.createFood();

理解:同一个工厂的不同方法,生产不同产品(不限共同父类)
JDK实例: java.sql.DriverManager.getConnection()会调用driver.connect(url),得到一个数据库连接;driver是数据库驱动,即具体工厂,其抽象工厂为java.sql.Driver,数据库连接即具体产品,其抽象产品为java.sql.Connection

4、建造模式(Builder)

public class ConcreteBuilder extends AbstractBuilder{
    private Product product = new Product();
    public void buildPartA() {
        product.setPartA("A");
    }
    public void buildPartB() {
        product.setPartB("B");
    }
    public Product getProduct() {
        return product;
    }
}

理解:用一个类来构建另一个类的实例,提取构建过程,方便复用
JDK案例:StringBuilder和StringBuffer用来建造String

5、原型模式(Prototype)

理解:以自己为原型创建新对象,即克隆
JDK案例:java.lang.Object#clone()、继承java.lang.Cloneable的类

6、适配器模式(Adapter)

类适配器

public class Adapter extends A implements B {
    // 本类要实现的b1方法,要用到A中现成的方法,就可以extends A,然后就可以调用a1
    public void b1() {   
         a1();
     }
}

对象适配器

public class Adapter implements B {
    private A a;
    public Adapter(A a){
        this.a = a;
    }
    // 本类要实现的b1方法,要用到A中现成的方法,就可以包含一个A实例,然后就可以调用a1
    public void b1() { 
        ...
        a.a1();
        ...
    }
}

理解:通过继承A或包含A,来让B的子类能调用A中现成的方法
JDK案例:
1、java.io.InputStreamReader(InputStream),字符流InputStreamReader在构造时得到字节流InputStream的实例,InputStreamReader.read()中即可调用InputStream.read()
2、java.io.OutputStreamWriter(OutputStream)

7、桥接模式(Bridge)

public class Bridge{
    private A a;              //抽象类
    public void setA(A a) {
        this.a = a;
    }
    protected void operation(){
        ...
        a.a1();                  // a1()的行为取决于a具体是什么对象
        ...
    }
}
使用:
Bridge bridge = new Bridge();
bridge.setA(new A1());
bridge.operation();
bridge.setA(new A2());
bridge.operation();

理解:通过接收不同的A对象,来让本类有不同的表现
与对象适配器的区别:对象适配器是想利用A,桥接器是想接收不同的A
JDK案例:java.util.logging.Handler.setFilter(Filter)

8、组合模式(部分整体模式,Composite)

public class File extends Node {    //叶节点
    public void display() {  }
}
public class Folder extends Node {  //枝节点
    List nodeList = new ArrayList();  //枝节点包含节点列表
    public void display() {
        for(Node node:nodeList){
            node.display();
        }
    }
}

理解:组合模式适用于树状结构,叶节点和枝节点继承相同的父类,枝节点包含一个节点列表,方便递归访问
JDK案例:java.awt包中Container和Button都继承自Component,同时Container里包含Component列表

9、装饰者模式(Decoration)

// 被装饰者
public class Chicken extends Food {  
    private String desc = "鸡肉";
    public String getDesc() {
        return desc;
    }
}
// 装饰者
public class SteamedFood extends Food {  
    private Food food;
    public SteamedFood(Food f){
        this.food = f;
    }
    public String getDesc() {
        return "蒸" + food.getDesc();
    }
}
使用:
Food fod = new Chicken();
Food sf = new SteamedFood(food);  //装饰
sf.getDesc();

理解:目的是对原对象进行一些改造,并且改造后得到的类型不变
JDK案例:
1、java.io.BufferedInputStream(InputStream),BufferedInputStream是InputStream的子类
2、java.io.BufferedOutputStream(OutputStream),BufferedOutputStream是OutputStream的子类

10、外观模式(门面模式,Facade)

class Faced
{
    SubSystemOne one;  // 子系统
    SubSystemTwo two;
    SubSystemThree three;
    public Faced()
    {
        one = new SubSystemOne();
        two = new SubSystemTwo();
        three = new SubSystemThree();
    }
    public void MethodA()
    {
        one.MethdOne();
         two.MethodTwo();
    }
     public void MethodB()
    {
        one.MethdOne();
        three.MethdThree();
    }
}

理解:门面类 组合 一些子系统对象,供外界统一访问
JDK案例:JDBC 封装了许多操作数据库的子系统

11、享元模式(Flyweight)

// 享原类
public class ConcreteFlyweight implements Flyweight {
    private String state = null;
    public ConcreteFlyweight(String state){
        this.state = state;
    }
    public void operation() {
    }
}
// 享元工厂类
public class FlyweightFactory {
    // 享元Map
    private Map flyweightMap= new HashMap();
    public Flyweight factory(String state){
        Flyweight fly = flyweightMap.get(state);
        if(fly == null){
            fly = new ConcreteFlyweight(state);
            flyweightMap.put(state, fly);
        }
        return fly;
    }
}

理解:享元工厂维护一个享元Map,外界想要享元对象时,享元工厂先检查是否已有该state的对象,否才创建新享元对象
JDK案例:
1、java.lang.Integer#valueOf(int),Integer、Byte、Short、Long缓存了-128到127的整数
2、java.lang.Character#valueOf(char),Character缓存了0到127的码点

12、代理模式(Proxy)

静态代理

// 服务类
public class BuyHouseImpl implements BuyHouse {
    public void buyHosue() {
        System.out.println("我要买房");
    }
}
// 静态代理类
public class BuyHouseProxy implements BuyHouse {
    private BuyHouse buyHouse;
    public BuyHouseProxy(final BuyHouse buyHouse) {
        this.buyHouse = buyHouse;
    }
    public void buyHosue() {
        ...
        buyHouse.buyHosue();
        ...
    }
}
使用:
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();

理解:类似于装饰器模式

动态代理

// 动态处理器
public class DynamicProxyHandler implements InvocationHandler {  // java.lang.reflect.InvocationHandler
    private Object object;
    public DynamicProxyHandler(final Object object) {
        this.object = object;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ...
        Object result = method.invoke(object, args);
        ...
        return result;
    }
}
使用:
BuyHouse buyHouse = new BuyHouseImpl();
// 动态处理器实例
DynamicProxyHandler dph = new DynamicProxyHandler(buyHouse);
// 生成代理实例
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
    Class[]{BuyHouse.class}, dph); // 第二个参数为接口数组,等同于 BuyHouseImpl.getClass().getInterfaces()
proxyBuyHouse.buyHosue();

理解:静态代理中,静态代理类只能代理一个服务类的指定方法;动态代理中,一个动态处理器,可以代理任何服务类的,父接口中有定义的方法
JDK案例:java.lang.reflect.Proxy

CGLIB代理

CGLIB(Code Generation Library)是一个开源类库

// 动态处理器
public class CglibProxy implements MethodInterceptor {  // net.sf.cglib.proxy.MethodInterceptor
    private Object target;
    public Object getInstance(final Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();  // net.sf.cglib.proxy.Enhancer
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    // net.sf.cglib.proxy.MethodProxy
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 
        ...
        Object result = methodProxy.invoke(object, args);
        ...
        return result;
    }
}
使用:
BuyHouse buyHouse = new BuyHouseImpl();
// 动态处理器实例
CglibProxy cp = new CglibProxy();
// 生成代理实例
BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cp.getInstance(buyHouse);
buyHouseCglibProxy.buyHosue();

理解: CGLib通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。CGLIB动态代理的性能更高,但创建代理对象所花费的时间多。所以,无需频繁创建代理对象,用CGLIB合适,反之JDK合适。JDK动态代理只能代理接口中有定义的方法,CGLib动态代理只能代理非final类的方法。

13、策略模式(Strategy)

public class Context {
    private A a;         // 抽象策略类
    public Context(A a){
        this.a= a;       // 持有具体策略对象
    }
    public void operation(){
        ...
        a.a1();   // 执行效果取决于,持有的具体策略对象
        ...
    }
}

理解:通过接收不同的A对象,来让自己的方法有不同的效果
与桥接模式的区别:桥接模式是让a对象成为自己的一部分,策略模式只是要利用a的方法(算法)
JDK案例:
1、java.util.Comparator#compare()
2、javax.servlet.http.HttpServlet
3、javax.servlet.Filter#doFilter()

14、模板方法模式(Template Method)

// 抽象类作为模板
public  abstract class Template{  
    final void cookProcess(){     // final使得不被复写
         this.HeatOil();
         this.pourVegetable();
         this.fry();
    }  
    void  HeatOil(){                   // 模板中实现部分方法
        System.out.println("热油");  
    }  
    abstract void  pourVegetable();
    abstract void  fry();
}
public class BaoCai extend  Template{
      public void  pourVegetable(){  
          System.out.println(”下包菜“);  
      }  
      public void  fry(){  
          System.out.println(”炒包菜“);  
      }  
}
使用:
BaoCai baoCai = new BaoCai();
baoCai.cookProcess();

理解:抽象实现部分方法,以便复用;子类重写部分方法,得以定制
JDK案例:
1、java.util.AbstractList 抽象类实现了部分方法,其子类ArrayList重写了原抽象方法get()
2、java.io.InputStream 抽象类实现了部分方法,其子类FileInputStream重写了原抽象方法read()

15、观察者模式(发布订阅模式,Observer)

// 被观察者类
public class WechatServer implements Observerable {
    private List list = = new ArrayList();  // 维护一个观察者列表
    private String message;
    public void registerObserver(Observer o) { // 注册观察者
        list.add(o);
    }
    public void removeObserver(Observer o) { // 移除观察者
            list.remove(o);
    }
    public void notifyObserver() {    // 通知所有观察者
        for(int i = 0; i < list.size(); i++) {
            Observer oserver = list.get(i);
            oserver.update(message);
        }
    }
    public void setMessage(String s) {
        this.message = s;
        notifyObserver();
    }
}
// 观察者类
public class User implements Observer {
    public void update(String message) {
        System.out.println(" 收到推送消息: " + message);
    }
}

理解:被观察者维护一个观察者列表,可以通过调用观察者的方法,来向所有观察者传递信息
JDK案例:实现 标记接口 java.util.EventListener 的类,即被观察者

16、迭代器模式(Iterator)

理解:迭代器持有集合,并提供方法 hasNext()、next() 来顺序访问集合
JDK案例:实现 java.util.Iterator 的类

17、责任链模式(Chain Of Responsibility)

// 责任人(处理者)类
public class ConcreteHandler1 extends Handler {
    protected Handler successor;   // 下一个责任人
    public Handler getSuccessor() {
        return successor;
    }
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public void handleRequest() {
        System.out.println("处理请求");
        if(getSuccessor() != null)
        {
             // 交给责任链里的下一个责任人继续处理
            getSuccessor().handleRequest();     
        }
    }
}
使用:
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setSuccessor(handler2);
handler1.handleRequest();

理解:一个责任人持有下一个责任人,自己处理完后就把请求传递给下一个责任人,责任人之间的解耦
JDK案例:
1、java.util.logging.Logger#log()
2、javax.servlet.Filter#doFilter()

18、命令模式(Command)

// 命令接收者类
public class Receiver
{
     public void Do()
     {
     }
}
// 命令类
public class ConcreteCommand implements ICommand
{
    private Receiver receiver = null;
    public ConcreteCommand(Receiver receiver)
    {
        this.receiver = receiver;
    }
    public void Execute()
    {
        this.receiver.Do();
    }
}
// 命令调用者类
public class Invoker
{
    private ICommand command = null;
    public void SetCommand(ICommand command)
    {
        this.command = command;
    }
    public void RunCommand()
    {
        command.Execute();
    }
}
使用:
Receiver receiver = new Receiver();
Invoker invoker = new Invoker();
invoker.SetCommand(new ConcreteCommandA(receiver)); // 调用A命令
invoker.RunCommand();
invoker.SetCommand(new ConcreteCommandB(receiver)); // 调用B命令
invoker.RunCommand();

理解:命令调用者 持有 命令,命令 持有 命令接收者。解耦调用者和接收者,在不改变命令接收者的情况下,可以通过新增和改动命令,来增强功能。
与装饰器模式的区别:装饰器模式强调属性增强,命令模式强调方法增强
JDK案例:java.lang.Runnable 即命令类的父接口

19、备忘录模式(Memento,快照模式,Snapshot)

// 备忘录
public class Memento {
    private int state;
    public Memento(int state) {
        this.state = state;
    }
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
    }
}
// 备忘录管理者
public class Caretaker {
    private Memento memento;
    public Memento getMemento() {
        return this.memento;
    }
    public void saveMemento(Memento memento) {
        this.memento = memento;
    }
}
// 备忘录发起者
public class Originator {
    private int state = 0;
    public Memento createMemento() {
        return new Memento(state);
    }
    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
    }
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
    }
}
使用:
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState(3);
// 发起者产生快照(备忘录),并由管理者保存
caretaker.saveMemento(originator.creatMemento());  
originator.setState(5);
// 发起者接收一个备忘录,并恢复回备忘录记录的状态
originator.restoreMemento(caretaker.retrieveMemento());  

理解:发起者可以产生和接收一个备忘录,管理者可以保存一个备忘录,发起者和管理者独立存在
JDK案例:java.util.Date的getTime() 和 setTime()相当于生成快照 和 恢复到快照记录的状态

20、状态模式(State)

public class Door implements DoorInterface {
    public final static int OPENING_STATE = 1;
    public final static int CLOSING_STATE = 2;
    private int state;
    public void setState(int state) {
        this.state = state;
    }
    public void close() {
        // 根据 state 做相应处理
    }
    public void open() {
        // 根据 state 做相应处理
    }
}
使用:
 DoorInterface door = new Door();
 lift.setState(Door.OPENING_STATE);
 lift.open();
 lift.close();

理解:通过改变对象内部的状态,来控制其行为
JDK案例:java.util.Iterator的next()即改变其状态

21、访问者模式(Visitor)

// 元素类(被访问者)
class ConsumeBill implements Bill {
    private double amount;
    public ConsumeBill(double amount) {
        this.amount = amount;
    }
    public void accept(AccountBookVisitor visitor) {
        visitor.visit(this);   // 元素 接受 访问者访问时,回调访问者,将自己作为参数传递给访问者
    }
    public double getAmount() {
        return amount;
    }
}
// 访问者类
class Boss implements AccountBookVisitor {
    private double totalConsume;
    // 访问者 的 访问方法 由 被访问者调用,参数即被访问者
    public void visit(ConsumerBill consumerBill) {
        totalConsumer = totalConsumer + consumerBill.getAmount();  // 将访问结果 保存到 成员变量里
    }
    public void getTotalConsumer() {
        System.out.println(totalConsumer);
    }
}
// 对象结构 类(元素管理器)
class AccountBook {
    private List listBill = new ArrayList(); // 对象结构维护一个被访问者列表
    public void add(Bill bill) {
        listBill.add(bill);
    }
    public void accept(AccountBookVisitor viewer) {
        for (Bill b : listBill) {
            b.accept(viewer);
        }
    }
}
使用:
// 元素管理器 添加元素
AccountBook accountBook = new AccountBook();
accountBook.add(new ConsumeBill("消费", 3000));
accountBook.add(new ConsumeBill("消费", 4000));
// 访问者
Boss boss = new Boss();
// 元素管理器 接受 访问,即所有元素接受访问
accountBook.accept(boss);
// 访问完 即 得到结果
boss.getTotalConsumer();

理解:元素管理器维护一个元素集合,元素管理器接受访问者访问,即所有元素接受访问;元素接受访问,即回调访问的访问方法。使得,在不改变元素和元素管理器的情况下,可以通过新增或更改访问者,来实现业务逻辑,且访问者中不需要遍历元素。
JDK案例:javax.lang.model.element.ElementVisitor 和 Element 即一对访问者 和 被访问者

22、中介者模式(调停者模式,Mediator)

// 同事类A
public class ConcreteColleagueA extends Colleague{
    protected Mediator mediator;
    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }
    public void notifyColleagueB() {
        mediator.notifyColleagueB();
    }
    public void operation() {    }
}
// 同事类B
public class ConcreteColleagueB extends Colleague{
    protected Mediator mediator;
    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }
    public void notifyColleagueA() {
        mediator.notifyColleagueA();
    }
    public void operation() {    }
}
// 中介者
public class ConcreteMediator extends Mediator{
    protected Colleague colleagueA;
    protected Colleague colleagueB;
    public Mediator(Colleague colleagueA, Colleague colleagueB) {
        this.colleagueA = colleagueA;
        this.colleagueB = colleagueB;
    }
    public void notifyColleagueA() {      // 中介提供调用同事A的方法
            colleagueA.operation();
    }
    public void notifyColleagueB() {    // 中介提供调用同事B的方法
            colleagueB.operation();
    }
}
使用:
ConcreteColleagueA colleagueA = new ConcreteColleagueA();
ConcreteColleagueB colleagueB = new ConcreteColleagueB();
// 中介 持有 两个同事
Mediator mediator = new ConcreteMediator(colleagueA, colleagueB);
// 同事持有中介
colleagueA.setMediator(mediator);
colleagueB.setMediator(mediator);
// 同事可以通过调用中介 来间接调用另一个同事
colleagueA.notifyColleagueB();
colleagueB.notifyColleagueA();

理解:中介持有两个同事,并分别提供调用两个同事的方法;两个同事都持有中介,可以通过调用中介提供的方法,间接调用另一个同事
JDK案例:java.util.Timer 作为中介者,能持有多个同事,并调度

23、解释器模式(Interpreter)

理解:特定用于语法解释器,即,输入一个字符串,输出一个对象或数值
JDK案例:java.util.Pattern

你可能感兴趣的:(JAVA设计模式)