美文网首页
JAVA设计模式

JAVA设计模式

作者: saoraozhe3hao | 来源:发表于2018-08-01 10:27 被阅读0次

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

    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<Node> nodeList = new ArrayList<Node>();  //枝节点包含节点列表
        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<String, Flyweight> flyweightMap= new HashMap<String, Flyweight>();
        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<Observer> list = = new ArrayList<Observer>();  // 维护一个观察者列表
        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<Bill> listBill = new ArrayList<Bill>(); // 对象结构维护一个被访问者列表
        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设计模式

          本文链接:https://www.haomeiwen.com/subject/qbelvftx.html