专栏首页关忆北.Java设计模式

Java设计模式

设计模式

一、单例模式

定义:单例模式,保证一个类有且仅有一个实例,并提供一个它的全局访问点。

缺点:不管用到与否,类装载时就完成实例化(如果没有用到,则内存浪费)。

public class Singleton {
    private final static Singleton instance = new Singleton();

    /**
     * 构造方法是private,其他类无法new,
     * 所以其他类在试用该类的实例时,只能通过getInstance()来获取
     */
    private Singleton() {
    }
    
    public static Singleton getInstance() {
        return instance;
    }

    public void s() {
        System.out.println("s");
    }

    public static void main(String[] args) {
        //调用方式
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }
}

单例模式之饿汉模式

单例模式是线程安全的,类加载到内存后,仅实例化一个实例,JVM保证线程安全。

原因:JVM保证每一个Class只Load到内存一次,static修饰的变量是在Class在加载到内存后马上进行初始化的。唯一实例被final修饰表,则对象不可变,被static修饰,静态变量被所有对象所共享,内存中只有一个副本。

单例模式之懒汉模式

按需初始化,但是多线程会有线程安全问题。

public class LazyLoading {
    private static LazyLoading INSTANCE;

    private LazyLoading() {
    }
	
    //可以使用synchronized修饰static语句,但是效率会降低
    private static LazyLoading getInstance() {
        if (INSTANCE == null) {
            try {
                //模拟被其他线程打断
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new LazyLoading();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                //打印对象的hash码,同一个类的不同对象的hashcode是不同的,判断对象是否为同一对象
                System.out.println(LazyLoading.getInstance().hashCode());
            }).start();
        }
    }
}

hashcode解读: 1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的; 2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同; 3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点; 4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们**“存放在同一个篮子里”**。1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的; 2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同; 3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点; 4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们**“存放在同一个篮子里”**。

通过匿名内部类的方式实现单例

/**
 * @author Liutx
 * @date 2020/11/17 21:42
 * @Description 优点:如果只加载Test3的话,Test3的唯一实例是不会被初始化的,
 * 内部的静态内部类在加载时是不会被加载的,只有在调用getInstance时才会被加载
 * JVM保证线程安全,Test3Holder只被加载一次
 */
public class Test3 {
    private Test3() {
    }

    /**
     * 静态内部类初始化外部类的对象
     */
    private static class Test3Holder {
        private final static Test3 INSTANCE = new Test3();
    }

    public static Test3 getInstance() {
        return Test3Holder.INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Test3.getInstance().hashCode());
            }).start();
        }
    }
}

枚举实现单例–《Effective Java》写法(最完美写法)

/**
 * @author Liu-PC
 * 解决线程同步,防止反序列化
 */

public enum EFSingleton {
    INSTANCE;

    public void m() {
        System.out.println("业务方法");
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            Thread.sleep(500);
            new Thread(() -> {
                System.out.println(EFSingleton.INSTANCE.hashCode());
            }).start();
        }
    }
}

Java通过反射的方式把整个.class文件加载到内存中去创建一个单例对象。使用枚举单例不会被反序列化的原因是因为枚举类没有构造方法,枚举是一个abstract class,枚举是一个abstract class,即使是拿到class文件也无法创建对象。

二、策略模式

策略模式的三个角色
  • Strategy:抽象策略角色,对算法、策略的抽象,定义每个算法、策略所必需的方法,通常为接口。
  • ConcreteStrategy:具体策略角色,实现抽象策略角色,完成具体的算法、策略。
  • Context:上下文环境角色,保存了ConcreteStrategy,负责调用ConcreteStrategy。
/**
 * @author Liutx
 * @date 2020/11/19 21:57
 * @Description
 */
public class CalculateContext {
    private CalculateStrategy calculateStrategy;

    /**
     * 构造方法,使用策略对象
     */
    public CalculateContext(CalculateStrategy calculateStrategy) {
        this.calculateStrategy = calculateStrategy;
    }

    /**
     * 负责调用策略
     *
     * @param num1
     * @param num2
     * @return
     */
    public int executeStrategy(int num1, int num2) {
        return calculateStrategy.doOperation(num1, num2);
    }
}
/**
 * @author Liu-PC
 * 策略角色
 */
public interface CalculateStrategy {
    /**
     * 策略计算接口
     * @param firstNum
     * @param secNum
     * @return
     */
    public int doOperation(int firstNum, int secNum);
}
/**
 * @author Liutx
 * @date 2020/11/19 22:01
 * @Description 策略的不同实现,相当于if else中不同的逻辑代码
 * 本demo使用加减乘除代替不同的策略逻辑
 */
public class OperationAdd implements CalculateStrategy {
    @Override
    public int doOperation(int firstNum, int secNum) {
        int result = firstNum + secNum;
        System.out.println(result);
        return result;
    }
}
/**
 * @author Liutx
 * @date 2020/11/19 22:04
 * @Description
 */
public class OperationMultiply implements CalculateStrategy {
    @Override
    public int doOperation(int firstNum, int secNum) {
        int result = firstNum * secNum;
        System.out.println(result);
        return result;
    }
}
/**
 * @author Liutx
 * @date 2020/11/19 22:05
 * @Description
 */
public class OperationSubtract implements CalculateStrategy {
    @Override
    public int doOperation(int firstNum, int secNum) {
        int result = firstNum - secNum;
        System.out.println(result);
        return result;
    }
}
/**
 * @author Liutx
 * @date 2020/11/19 22:07
 * @Description
 */
public class Usage {

    public static void main(String[] args) {
        //使用策略模式,创建上下文对象即可,根据不同的上下文调用不同的策略接口

        //调用相加策略
        CalculateContext context = new CalculateContext(new OperationAdd());
        context.executeStrategy(10, 5);

        context = new CalculateContext(new OperationMultiply());
        context.executeStrategy(10, 5);

        context = new CalculateContext(new OperationSubtract());
        context.executeStrategy(10, 5);
    }
}

关于SpringBoot集成使用策略模式请翻阅我的另一篇博客,上边详细介绍了使用方式

基于SpringBoot的策略模式demo

三、工厂模式

定义:任何可以产生对象的方法或类,都可以称之为工厂。单例也是一种工厂----静态工厂。

  1. 一个抽象产品类
  2. 多个具体产品类
  3. 一个抽象工厂
  4. 多个具体工厂 - 每一个具体产品对应一个具体工厂
  5. 符合 - OCP开放封闭原则
优点
  1. 降低了代码耦合度,对象的生成交给子类去完成
  2. 实现了开放封闭原则 - 每次添加子产品 不需要修改原有代码
缺点
  1. 增加了代码量,每个具体产品都需要一个具体工厂
  2. 当增加抽象产品 也就是添加一个其他产品族 需要修改工厂 违背OCP

工厂模式之简单工厂

简单工厂又称静态工厂

/**
 * @author Liutx
 * @date 2020/11/30 21:38
 * @Description 生产工具工厂,工厂可以生成不同的工具对象
 */
public class VehicleFactory {
    /**
     * 自定义生产过程,前边可以加日志处理、权限处理等
     *
     * @return
     */
    public Car createCar() {
        return new Car();
    }

    public Broom createBroom() {
        //扫帚的权限控制省略
        return new Broom();
    }

    public Plane createPlane() {
        return new Plane();
    }
}
缺点
  1. 扩展性不好,没新增一种工厂,需要新增一个工厂类。违反OCP开放封闭原则

新增一个工厂方法:CarFactory

public class CarFactory {
    public Car create() {
        //日志框架
        System.out.println("a car created!");
        return new Car();
    }
}
简单工厂可以完成任意定制不同的工厂实现(生产出不同的对象),比如Car、Plane等,可以实现任意定制生产过程(可以在生成的工厂对象的逻辑中自定义权限、日志、其他业务逻辑等),但是无法实现任意定制产品一族,任意定制产品一族可以使用抽象工厂。

工厂模式之抽象工厂

工厂模式之抽象工厂

是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。类继承自抽象工厂,即可生产指定对象。新增实体的时候无需修改已有代码。比如食物这个工厂,在现实世界中,它可以生产面包、鸡蛋、肉等,在魔法世界中,食物工厂可以生产魔法豆、会唱歌的火鸡、HP口服液等,我们把食物比作是最大的一个抽象工厂,现实世界(ModernFactory)抽象工厂和魔法世界(MagicFactory)抽象工厂继承自最大的食物抽象工厂,然而bread面包、egg鸡蛋、meat肉类都继承自ModernFactory抽象工厂、MagicBeans魔法豆等继承自MagicFactory,我们把食物的世界分为了现实和魔法两族,而这两族中又分为了面包、鸡蛋、火腿肠、啤酒、饮料、矿泉水等更为具体族(对象),当我们任意定制新的产品一族时就无需再修改已有代码,只需根据产品分类不同,继承自不同的工厂即可,一步步化抽象为具体,抽象工厂实际最大的好处就是实现了代码的解耦。

talk is cheaper,show me your code.

Abstractory.class

/**
 * @author Liutx
 * @date 2020/12/8 22:18
 * @Description
 */
public abstract class Abstractory {
    /**
     * 抽象工厂可以生产食物
     * @return
     */
    abstract Food createFood();
}

Food.class

public abstract class Food {
    abstract void printName();
}

MagicBeans.class

public class MagicBeans extends Food{

    @Override
    void printName() {
        System.out.println("I create MagicBeans");
    }
}

Bread.class

public class Bread extends Food{
    @Override
    void printName() {
        System.out.println("I create Bread");
    }
}

MagicFactory.class

public class MagicFactory extends Abstractory {

    @Override
    Food createFood() {
        return new MagicBeans();
    }
}

ModernFactory.class

public class ModernFactory extends Abstractory {

    @Override
    Food createFood() {
        return new Bread();
    }
}

实例解读:抽象工厂可以创建食物对象,食物大类分为现实世界的面包和魔法世界的魔法豆,面包是由现实工厂生产的,魔法豆是由魔法工厂生产的,魔法豆一族和面包一族分别继承自食物,通过代码工厂就可以根据需求生产不同的食物(调用不同的方法)。

Main.class

public class Main {
    public static void main(String[] args) {
        Abstractory mg = new MagicFactory();
        Food mgFood = mg.createFood();
        mgFood.printName();

        System.out.println("======================魔法与现实的分界线=======================");

        Abstractory md = new ModernFactory();
        Food mdFood = md.createFood();
        mdFood.printName();
    }
}

输出:

I create MagicBeans
======================魔法与现实的分界线=======================
I create Bread

外观(门面)模式

外观模式(Facade),他隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。这种类型的设计模式属于结构性模式。为子系统中的一组接口提供了一个统一的访问接口,这个接口使得子系统更容易被访问或者使用。类似的实际例子有消息中间件,把一个数据丢到消息中间件,谁需要,谁去消息后中间件去拿。这种设计模式可以用于解耦。

我们举个栗子,你要去工厂制造一辆汽车,你需要先从门口去一楼制造引擎,再从门口去二楼制造底盘,最后从门口去五楼制造变速箱,然后去四楼制造轮胎,这样整个流程会特别繁琐,外观模式就提供一个门面接口(Facade),门面内把原来的逻辑流程进行封装,你只需要从门口去到这个门面,门面内部分工帮你去管理汽车的制造流程。回归代码世界,如果我们不使用门面模式,需要调用发动机制造的对象、底盘制造的对象变速箱制造的对象等等,这样你的业务逻辑便显得杂乱无章并且代码各个子系统之间的耦合度很高,显然这样做不是最优方案。

门面解决方案:

将汽车制造流程逻辑进行封装,只提供一个汽车制造接口,当客户访问汽车接口(Facade)时,工厂内部流水线制造组装完成一辆用户所需的汽车。这样做的优点就是:把多个对象之间的调用交互变为一个对象与一个接口之间的交互,降低代码耦合度。与用户分别调用子系统模块相比,客户不能自己定义引擎、底盘等配件的型号,所以门面模式提供的功能有限,但是它可以快速且简便的提供客户真正关心的需求。

talk is cheaper,show me your code.

假设我们有一个需求:将ogg格式的视频文件转换为mp4格式,那么我们需要读取ogg格式的文件流,将流转换为音频流、视频流,写入文件,再进行音频流、视频流组合,最后完成转换,当然我们可以单个流程进行调用,下面的代码中有两种方式的具体体现。

代码已经开源至Github https://github.com/FirstMrRight/Design_pattern

Codec.java

/**
 * @author  Liutx
 * @date  2020/12/26 22:37
 * @Description 解码器接口
 */
public interface Codec {
}

MPEG4CompressionCodec.java

/**
 * @author Liutx
 * @date 2020/12/26 22:38
 * @Description MPEG4解码器
 */
public class MPEG4CompressionCodec implements Codec {

    /**
     * 该解码器能够行使的功能
     */
    public String type = "mp4";
}

OggCompressionCodec.java

/**
 * @author Liutx
 * @date 2020/12/26 22:40
 * @Description ogg解码器
 */
public class OggCompressionCodec implements Codec {
    public String type = "ogg";
}

BitrateReader.class

/**
 * @author Liutx
 * @date 2020/12/26 22:48
 * @Description 字节读取转换为Video
 */
public class BitrateReader {
    public static VideoFile read(VideoFile file, Codec codec) {
        System.out.println("BitrateReader: reading file...");
        return file;
    }

    public static VideoFile convert(VideoFile buffer, Codec codec) {
        System.out.println("BitrateReader: writing file...");
        return buffer;
    }

}

AudioMixer.java

/**
 * @author Liutx
 * @date 2020/12/26 22:50
 * @Description 混音器,组合视频、音频
 */
public class AudioMixer {
    public File fix(VideoFile result){
        System.out.println("AudioMixer: fixing audio...");
        return new File("tmp");
    }
}

VideoFile.java

/**
 * @author Liutx
 * @date 2020/12/26 22:33
 * @Description
 */
public class VideoFile {
    private String name;
    private String codecType;

    public VideoFile(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCodecType() {
        return codecType;
    }

    public void setCodecType(String codecType) {
        this.codecType = codecType;
    }
}

VideoConversionFacade.java

/**
 * @author Liutx
 * @date 2020/12/26 22:58
 * @Description 门面模式封装内部流程、对外提供接口
 */
public class VideoConversionFacade {
    public File convertVideo(String fileName, String format) {
        System.out.println("VideoConversionFacade: conversion started.");
        VideoFile file = new VideoFile(fileName);
        Codec sourceCodec = CodecFactory.extract(file);
        Codec destinationCodec;
        if (format.equals("mp4")) {
            destinationCodec = new OggCompressionCodec();
        } else {
            destinationCodec = new MPEG4CompressionCodec();
        }
        VideoFile buffer = BitrateReader.read(file, sourceCodec);
        VideoFile intermediateResult = BitrateReader.convert(buffer, destinationCodec);
        File result = (new AudioMixer()).fix(intermediateResult);
        System.out.println("VideoConversionFacade: conversion completed.");
        return result;
    }
}

ClientDemo.java (调用)

public class ClientDemo {
    public static void main(String[] args) {
        VideoConversionFacade converter = new VideoConversionFacade();
        File mp4Video = converter.convertVideo("video.ogg", "mp4");


        System.out.println("===================================");


        VideoFile file = new VideoFile("video.ogg");
        CodecFactory.extract(file);
        BitrateReader.read(file,new OggCompressionCodec());
        BitrateReader.convert(file,new MPEG4CompressionCodec());
        AudioMixer audioMixer = new AudioMixer();
        audioMixer.fix(file);
    }
}

Console 输出:

VideoConversionFacade: conversion started.
CodecFactory: extracting ogg audio...
BitrateReader: reading file...
BitrateReader: writing file...
AudioMixer: fixing audio...
VideoConversionFacade: conversion completed.
===================================
CodecFactory: extracting ogg audio...
BitrateReader: reading file...
BitrateReader: writing file...
AudioMixer: fixing audio...

**总结:**使用门面模式可以非常优雅的实现代码的调用,当然我们也可以自己使用单独调用对象的方式实现相同的功能,但是这种方式不仅显得代码杂乱无章,而且这种方式一看就非常不环保。

装饰者模式

定义:装饰是一种结构设计模式, 允许你通过将对象放入特殊封装对象中来为原对象增加新的行为。

为对象增加新的行为可以理解为在原基础上的扩展,抛开设计模式而言,我们首先想到的解决方式是继承,如果你想给一款低配版版汽车装上倒车影像,你可以你的Car可以继承自RoadRadar ,当你想继续提升行使品质的时候,你希望在汽车上安装Bose音响,此时,显然Car不能再继承一个Bose类,因为Java不支持多继承,显然这样也不符合“对修改关闭,对扩展开放”的原则。

引入装饰着模式的思想,我们把Car作为一个被装饰者,RoadRadar、Bose 可以作为对Car的扩展,装饰者模式可以在不修改被装饰者的情况下,增加被修饰者的功能,比生成子类更加灵活。

PrefectCar prefectCar = new RoadRadarAndBoseCar(new RoadRadarCar(new Car));

可见,最初的被装饰者Car对象是始终是被包裹在最内层的,如果接下来再对Car进行包装,我们直接将prefectCar对象作为入参即可,这样便完成了在扩展功能的需求下,做到了不修改被装饰者的属性,符合“对修改关闭,对扩展开放”的原则。

被装饰者要与装饰者继承自同一超类

装饰者模式最显著的特征:可通过以当前类或对象为参数的创建方法或构造函数来识别。

引用:

装饰者模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。 在装饰者模式中,为了让系统具有更好的灵活性和可扩展性,我们通常会定义一个抽象装饰类,而将具体的装饰类作为它的子类 角色 Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。 ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。 Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。 ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。 由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。 装饰模式的核心在于抽象装饰类的设计。 作者:小旋锋 链接:https://juejin.cn/post/6844903681322647566 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

挪用refactoringguru上的栗子:文件读取写入流

talk is cheaper,show me your code.

DataSource.java: 定义了读取和写入操作的通用数据接口(本类只作为代码封装用)

package refactoring_guru.decorator.example.decorators;

public interface DataSource {
    void writeData(String data);

    String readData();
}

FileDataSource.java

/**
 * @author Liutx
 * @date 2020/12/27 13:40
 * @Description 接口实现类,将被装饰者的文件操作代码进行封装实现,是被装饰者的一部分
 */
public class FileDataSource implements DataSource {
    private String name;

    public FileDataSource(String name) {
        this.name = name;
    }

    @Override
    public void writeData(String data) {
        File file = new File(name);
        try (OutputStream fos = new FileOutputStream(file)) {
            fos.write(data.getBytes(), 0, data.length());
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
    }

    @Override
    public String readData() {
        char[] buffer = null;
        File file = new File(name);
        try (FileReader reader = new FileReader(file)) {
            buffer = new char[(int) file.length()];
            reader.read(buffer);
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
        return new String(buffer);
    }
}

DataSourceDecorator.java

/**
 * @author Liutx
 * @date 2020/12/27 13:48
 * @Description 抽象基础装饰(被装饰者)
 */
public class DataSourceDecorator implements DataSource{
    private DataSource wrappee;

    DataSourceDecorator(DataSource source) {
        this.wrappee = source;
    }

    @Override
    public void writeData(String data) {
        wrappee.writeData(data);
    }

    @Override
    public String readData() {
        return wrappee.readData();
    }
}

CompressionDecorator.java

/**
 * @author Liutx
 * @date 2020/12/27 14:09
 * @Description 压缩装饰者
 */
public class CompressionDecorator extends DataSourceDecorator {
    private int compLevel = 6;

    public CompressionDecorator(DataSource source) {
        super(source);
    }

    public int getCompressionLevel() {
        return compLevel;
    }

    public void setCompressionLevel(int value) {
        compLevel = value;
    }

    @Override
    public void writeData(String data) {
        super.writeData(compress(data));
    }

    @Override
    public String readData() {
        return decompress(super.readData());
    }

    private String compress(String stringData) {
        byte[] data = stringData.getBytes();
        try {
            ByteArrayOutputStream bout = new ByteArrayOutputStream(512);
            DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel));
            dos.write(data);
            dos.close();
            bout.close();
            return Base64.getEncoder().encodeToString(bout.toByteArray());
        } catch (IOException ex) {
            return null;
        }
    }

    private String decompress(String stringData) {
        byte[] data = Base64.getDecoder().decode(stringData);
        try {
            InputStream in = new ByteArrayInputStream(data);
            InflaterInputStream iin = new InflaterInputStream(in);
            ByteArrayOutputStream bout = new ByteArrayOutputStream(512);
            int b;
            while ((b = iin.read()) != -1) {
                bout.write(b);
            }
            in.close();
            iin.close();
            bout.close();
            return new String(bout.toByteArray());
        } catch (IOException ex) {
            return null;
        }
    }
}

EncryptionDecorator.java

/**
 * @author Liutx
 * @date 2020/12/27 13:53
 * @Description 加解密装饰者
 */
public class EncryptionDecorator extends DataSourceDecorator {
    public EncryptionDecorator(DataSource source) {
        super(source);
    }

    @Override
    public void writeData(String data) {
        super.writeData(encode(data));
    }

    @Override
    public String readData() {
        return decode(super.readData());
    }

    private String encode(String data) {
        byte[] result = data.getBytes();
        for (int i = 0; i < result.length; i++) {
            result[i] += (byte) 1;
        }
        return Base64.getEncoder().encodeToString(result);
    }

    private String decode(String data) {
        byte[] result = Base64.getDecoder().decode(data);
        for (int i = 0; i < result.length; i++) {
            result[i] -= (byte) 1;
        }
        return new String(result);
    }
}

Demo.java

public class Demo {
    public static void main(String[] args) {
        String salaryRecords = "Name,Salary\nJohn Smith,100000\nSteven Jobs,912000";
        DataSourceDecorator encoded = new CompressionDecorator(new DataSourceDecorator(new FileDataSource("out/OutputDemo.txt")));
        encoded.writeData(salaryRecords);
        //DataSource不要导错包,不是连接池的DataSource
        DataSource plain = new FileDataSource("out/OutputDemo.txt");

        System.out.println("- Input ----------------");
        System.out.println(salaryRecords);
        System.out.println("- Encoded --------------");
        System.out.println(plain.readData());
        System.out.println("- Decoded --------------");
        System.out.println(encoded.readData());
    }
}

Console输出:

- Input ----------------
Name,Salary
John Smith,100000
Steven Jobs,912000
- Encoded --------------
eJzzS8xN1QlOzEksquTyys/IUwjOzSzJ0DE0AAGu4JLUstQ8Ba/8pGIdS0MjoBAAfpEOqg==
- Decoded --------------
Name,Salary
John Smith,100000
Steven Jobs,912000

装饰者模式及典型应用

  • Java I/O 中的装饰者模式
  • spring session 中的装饰者模式
  • Mybatis 缓存中的装饰者模式
本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.csdn.net/weixin_42313773复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • java设计模式之代理模式【设计模式】

    张三想要去日本某公司买xxx,但是对于经费等等一系列的原因然后就放弃了这个念头,我刚好要去日本玩的,张三得知我要去日本,他就偷偷给我说,他想要一个size为D的...

    简单的程序员
  • java设计模式之工厂模式【设计模式】

    工厂,就是生产各种物品的一个资源管理器,它在我们生活中是非常常见了,比如电子厂生产手机零件,芯片什么的...

    简单的程序员
  • java — 设计模式

    Mister24
  • Java设计模式

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    村雨遥
  • Java 设计模式

    村雨遥
  • java设计模式(4)-原型设计模式

    这篇推文学习创建型模式最后一种-原型模式,该模式的思想是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象,以便使用

    爱敲代码的猫
  • Java 设计模式

    云扬四海
  • JAVA 设计模式 策略模式

    用途 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。 策略模式是一种行为型模式。 结构 ? ...

    静默虚空
  • JAVA 设计模式 代理模式

    用途 代理模式 (Proxy) 为其他对象提供一种代理以控制对这个对象的访问。 代理模式是一种结构型模式。 结构 ? 图-代理模式结构图 Subject :...

    静默虚空
  • Java设计模式-代理模式

    作者:Jet啟思 链接:https://juejin.im/post/5a1e7ae16fb9a0451170e446 一、代理模式 定义 为其它对象提供一个...

    Java高级架构
  • JAVA设计模式-策略模式

    策略模式:定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户

    DH镔
  • java设计模式-工厂模式

    简单工厂模式又称为静态工厂方法模式,为何称为静态工厂方法模式呢?是因为该模式主要由一个工厂类的静态方法来提供各种产品。

    会说话的丶猫
  • Java 设计模式 代理模式

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://louluan.blog.c...

    亦山
  • Java 设计模式 工厂模式

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://louluan.blog.c...

    亦山
  • Java设计模式-组合模式

    组合模式: 将对象组合成树形结构以表示‘部分-整体’的层次结构, 使得用户对单个对象和组合对象的使用具有一致性. 解析 组合模式描述了如何将容器和叶子节点进行...

    Java帮帮
  • Java设计模式-命令模式

    在对象的结构和创建问题都解决了之后,就剩下对象的行为问题了: 如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高. 行为型模式共有1...

    Java帮帮
  • Java设计模式-策略模式

    策略模式: 定义一系列的算法, 将其一个个封装起来, 并使它们可相互替换, 使得算法可独立于使用它的客户而变化. ? (图片来源: 设计模式: 可复用面向对象...

    Java帮帮
  • JAVA 设计模式 命令模式

    用途 命令模式 (Command) 将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化; 对请求排队或请求日志,以及支持可撤销的操作。 命令模...

    静默虚空
  • Java设计模式-工厂模式

    简单来说工厂模式就是帮助我们创建对象,隐藏了对象创建过程的复杂度(如类B需要调用类A,工厂类提供A类的创建接口,B只需要调用接口传入相应参数即可)、避免你辛苦的...

    蒋老湿

扫码关注腾讯云开发者

领取腾讯云代金券