聊聊模板模式

聊聊模板模式

介绍

模板方法模式(Template Pattern),在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构情况下,重新定义算法中的某些步骤。

这个模式是用来创建一个算法的模板。什么是模板?这个方法将算法定义成一组步骤,其中任何步骤都可以是抽象的,由子负责实现。可以确保算法的结构保持不变,同时由子类提供部分实现。

以上部分内容摘抄自《Head Frist 设计模式》

这里的算法,可以理解为广义上的“业务逻辑”,并不一定是数据结构和算法中的“算法”。

实现模板方法也很简单,可以看看下面一个接收数据的示例。accept方法定义为final,是为了防止子类重写,打乱了顺序。convertData方法和saveDB定义为抽象方法(abstract),是为了让子类去实现。

// 数据协议类型
public enum DataProtocolEnum {
    SNAPSHOT,
    INDEX
}

// 模板方法类
public abstract class AbstractDataHandler<T> {
    public final void accept(DataProtocolEnum protocol, byte[] data) {
        T newData = convertData(protocol, data);
        saveDB(protocol, newData);
    }

    public abstract T convertData(DataProtocolEnum protocol, byte[] data);

    public abstract void saveDB(DataProtocolEnum protocol, T data);
}

// 模板方法实现类1
public class IndexDataHandler extends AbstractDataHandler<Index> {
    @Override
    public Index convertData(DataProtocolEnum protocol, byte[] data) {
        if (Objects.nonNull(protocol) && DataProtocolEnum.INDEX.equals(protocol)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                return objectMapper.readValue(data, Index.class);
            } catch (IOException e) {
                // error...
            }
        }
        return null;
    }

    @Override
    public void saveDB(DataProtocolEnum protocol, Index data) {
        System.out.println("Index数据,保存到数据库:" + data);
    }
}

// 模板方法实现类2
public class SnapshotDataHandler extends AbstractDataHandler<Snapshot> {
    @Override
    public Snapshot convertData(DataProtocolEnum protocol, byte[] data) {
        if (Objects.nonNull(protocol) && protocol.equals(DataProtocolEnum.SNAPSHOT)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                return objectMapper.readValue(data, Snapshot.class);
            } catch (IOException e) {
                // error
            }
        }
        return null;
    }

    @Override
    public void saveDB(DataProtocolEnum protocol, Snapshot data) {
        System.out.println("Snapshot数据,保存到数据库:" + data);
    }
}

测试一下:

public static void main(String[] args) {
    // 模拟,一个Server端
    ObjectMapper objectMapper = new ObjectMapper();
    Snapshot snapshot = new Snapshot("0001000", "随便一个名称", BigDecimal.ONE);
    Index index = new Index(BigDecimal.ONE, BigDecimal.ZERO, "000102");
    byte[] snapshotData = null;
    byte[] indexData = null;
    try {
        snapshotData = objectMapper.writeValueAsBytes(snapshot);
        indexData = objectMapper.writeValueAsBytes(index);
    } catch (JsonProcessingException e) {
        // error
    }

    // Snapshot客户端
    snapshotClient(snapshotData);
    // Index客户端
    indexClient(indexData);
}

private static void snapshotClient(byte[] data) {
    SnapshotDataHandler handler = new SnapshotDataHandler();
    handler.accept(DataProtocolEnum.SNAPSHOT, data);
}

private static void indexClient(byte[] data) {
    IndexDataHandler handler = new IndexDataHandler();
    handler.accept(DataProtocolEnum.INDEX, data);
}

...output

Snapshot数据,保存到数据库:Snapshot{code='0001000', name='随便一个名称', price=1}
Index数据,保存到数据库:Index{high=1, low=0, code='000102'}

为什么要使用模板方法呢?使用它的好处是啥?

使用模板方法最主要是,扩展和复用。

复用

模板方法抽象类中,定义了accept、convertData、saveDB,所有的数据都是通过accept去接收,然后调用convertData去做数据类型的一个转换,saveDB则是将数据存储到数据库中。虽然,本篇文章中只写了两种数据类型,而且它们的操作都是一样的没什么不同。

如果,不使用模板方法,后期数据类型增加了五六种,那就需要写五六种步骤相同的操作,太麻烦了,太多重复性的工作。使用了模板方法后,就会方便很多,将步骤定义好,实现类只需要去作具体的实现即可。

扩展

说说扩展,这种扩展指的是每个实现类中,虽然的步骤是不变的,但里面的行为操作却是不同。什么意思呢?

举个例子:在这些数据保存数据库时,Snapshot数据类型的数据量比较大,并且操作频率比较高,我想保存到数据库的同时,还要再保存一份到缓存里面。其他地方读取数据时,先读取缓存,缓存里面没有再去请求读取数据库的数据。 Index数据类型的数据,就比较简单,因为它的操作频率很低,只需要保存到数据库即可。

尾声

模板方法模式,除了本篇文章中列举的一个例子,还有其他的一些其他的框架或者工具也用到了模板方法模式。

比如:

  • Java Servlet中,有doGet、doPost、doPut等等一些方法,通过这些方法接收请求去执行业务逻辑
  • InputStream、OutPutStream,read、write之类的方法,它们有很多实现类也是如此。
  • Junit 框架,执行测试用例时,会执行before、after等方法,执行一些执行测试用例之前,前置、后置操作。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×