• 设计模式之模板方法


    前言

    本文介绍一下结构型模式中的模板方法。

    模板模式是什么

    定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现好了,这样不同的子类就可以定义出不同的步骤。通过继承的方式来实现模板 ,完成对父类的填充。

    UML

    模板方法

    代码案例

    public abstract class Greet {
    
        // 定义的模板方法,它作为算法的模板,final防止子类覆盖此方法
        // 某些方法是自己执行 某些方法是子类处理的
        final void all(){
            hello();
            bey();
            sleep();
        }
    
    
        protected abstract void hello();
    
        protected abstract void bey();
        
        // 相同部分
        // 也可以定义为 final 防止子类覆写,直接被模板方法使用或者让子类使用
        void sleep (){
            System.out.println("sleeping");
        }
        
        // 定义一个什么都不做的方法(或者默认的缺省实现),这种方法通常称为"hook" 钩子。子类自己视情况来决定要不要覆盖它们
        
        void hock(){}
    
    }
    

    钩子的用途:

    • 让子类更加灵活的实现逻辑,控制模板方法中的流程。
    • 可以让子类对模板方法中某些即将发生的(或刚刚发生的)步骤做出反应(有点AOP的意思)

    使用钩子的场景:如上所说,如果模板算法中的某些步骤子类必须实现的话,就是用抽象算法。如果算法中的这个部分是可选的,就用钩子。

    // 通过手机打招呼
    public class PhoneGreet extends Greet{
        @Override
        protected void hello() {
            System.out.println(" hello by phone ");
        }
    
        @Override
        protected void bey() {
            System.out.println(" bey by phone ");
        }
    }
    
    
    // 通过言语打招呼
    public class WordsGreet extends Greet {
        @Override
        protected void hello() {
            System.out.println("hello by mouth");
        }
    
        @Override
        protected void bey() {
            System.out.println("bey by mouth");
        }
    }
    
    // 测试类
    public class TemplateTest {
    
        public static void main(String[] args) {
            PhoneGreet greet = new PhoneGreet();
            // 调用模板方法
            greet.all();
        }
    }
    

    这样看来跟父类定义接口有什么区别?其中某些操作还是父类来进行的。

    JDK AbstractQueuedSynchronizer (AQS) 的使用

    仅简单介绍一下AQS 的使用,未涉及具体源码分析。后续单独写AQS的源码设计思路。

    AQS 是用来构建锁或者其他同步组件的基础框架。锁是面向使用者的,隐藏了实现细节,AQS是面向锁的实现者的,简化了锁的实现方式,对底层实现进行屏蔽。AQS的设计就是基于模板方法模式的。使用者需要继承同步器,并重写指定的方法。比如 CountDownLatch、Semaphore、RenntrantLock。

    下面代码演示 AQS 的使用

    // 自定义同步组件 在同一时刻只允许一个线程占有锁
    public class NewLock implements Lock {
        private static final int lockNum = 1;
    
        class Sync extends AbstractQueuedSynchronizer {
            // 是否处于独占状态
            @Override
            protected boolean isHeldExclusively() {
                return getState() == 1;
            }
    
            // 当状态为 0 的时候获取锁
            @Override
            public boolean tryAcquire(int acquire) {
                if (compareAndSetState(0, 1)) {
                    setExclusiveOwnerThread(Thread.currentThread());
                    return true;
                }
                return false;
            }
    
            // 释放锁
            @Override
            protected boolean tryRelease(int arg) {
                if (getState() == 0) {
                    throw new IllegalMonitorStateException();
                }
                setExclusiveOwnerThread(null);
                setState(0);
                return true;
            }
    
            // 返回Condition 每个Condition 都包含一个 condition 队列
            Condition newCondition() {
                return new ConditionObject();
            }
        }
    
        // 将锁的操作代理到 Sync 上
        private final Sync sync = new Sync();
    
        // 阻塞获取锁 
        @Override
        public void lock() {
            // acquire 会调用重写的 tryAcquire(int arg)
            sync.acquire(lockNum);
        }
    
        /**
         * 可中断地获取锁
         *
         * @throws InterruptedException
         */
        @Override
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(lockNum);
        }
    
        // 尝试非阻塞的获取锁
        @Override
        public boolean tryLock() {
            return sync.tryAcquire(lockNum);
        }
    
        // 超时获取锁
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return sync.tryAcquireNanos(lockNum, unit.toNanos(time));
        }
    
        // 释放锁
        @Override
        public void unlock() {
            sync.release(lockNum);
        }
    
        // 获取等待通知组件
        @Override
        public Condition newCondition() {
            return sync.newCondition();
        }
    
    
        // 是否获取到了锁
        public boolean isLocked(){
            return sync.isHeldExclusively();
        }
    
        // 是否有其他线程在等待获取锁
        public boolean hasQueuedThreads(){
            return sync.hasQueuedThreads();
        }
    }
    
    

    总结

    模板方法就是父类定义一系列的操作(模板),由子类来实现某些操作。这样可以规范子类提供某些步骤的实现。

    父类可以通过 final 来定义不想被修改的骨架。对于需要子类实现的抽象方法,一般声明为protected 这样可以使这些方法对外部不可以见,并且只用于子类继承可见。

    References

    • 《Java并发编程的艺术》
    • 《HEAD FIRST 设计模式 中文版》
  • 相关阅读:
    最大生成树与最小生成树
    有限制的最短路spfa+优先队列
    KM算法(最优匹配)
    网络最大流解方程组
    网络费用流-最小k路径覆盖
    树链剖分-点的分治(点数为k且距离最长的点对)
    树链剖分-点的分治(链的点的个数为k的点对数)
    树链剖分-点的分治(dis[i]+dis[j]==k的点对数量)
    树链剖分-点的分治(dis[i]+dis[j]<=k的点对数量)
    无向图欧拉通路
  • 原文地址:https://www.cnblogs.com/wei57960/p/12879436.html
Copyright © 2020-2023  润新知