技能篇:实际开发常用设计模式

技能篇:实际开发常用设计模式

创建型

单例模式

单例对象能节约系统资源,一个对象的创建和消亡的开销可能很小。但是日常的服务接口,就算是一般小公司也有十几万的QPS吧。每一次的功能运转都创建新的对象来响应请求,十几万对象的创建和销毁,想想就是一笔大开销,所以 spring 管理构造的 bean 对象一般都是单例。而且单例模式可以更好的解决并发的问题,方便实现数据的同步性

  • 优点
  • 在内存中只有一个对象,节省内存空间
  • 避免频繁的创建销毁对象,可以提高性能
  • 避免对共享资源的多重占用,简化访问
  • 为整个系统提供一个全局访问点
  • 缺点
  • 不适用于变化频繁的对象
//饿汉式
private static Singleton singleton = new Singleton();
//懒汉式
private static Singleton singleton;
public static Singleton getSingleton(){
    if (singleton == null) {
        singleton = new Singleton(); //被动创建,在真正需要使用时才去创建
    }
    return singleton;
}
//双重判断加锁机制
private volatile static Singleton instance;
//程序运行时创建一个静态只读的进程辅助对象
public static Singleton GetInstance() {
    //先判断是否存在,不存在再加锁处理
    if (instance == null){
        synchronized (Singleton.class){
            if(instance == null){
                instance = new Singleton();
            }
        }
    }
    return instance;
}
//静态初始化
private static readonly Singleton instance= new Singleton();
public static Singleton GetInstance(){
    return instance;
}

工厂模式

使用者不关心对象的实例化过程,只关心对象的获取。工厂模式使得产品的实例化过程和消费者解耦

  • 优点
  • 一个调用者想创建一个对象,只需通过其名称或其他唯一键值在工厂获取
  • 扩展性高,如果想增加生产一种类型对象,只要扩展工厂类就可以
  • 缺点
  • 工厂类不太理想,因为每增加一产品,都要在工厂类中增加相应的生产判断逻辑,这是违背开闭原则的
public interface Sender{  public void send();  }
public class MailSender implements Sender {
    @Override
    public void send() {
        System.out.println("this is mailsender!");
    }
}
public class SmsSender implements Sender {
    @Override
    public void send() {
        System.out.println("this is sms sender!");
    }
}
public class SendFactory {
    public Sender produce(String type) {
        if ("mail".equals(type)) {
            return new MailSender();
        } else if ("sms".equals(type)) {
            return new SmsSender();
        } else {
            return null;
        }
    }
    //若还有其他产品 则在工厂里加对应的 produce 方法
}

建造者模式

主要解决在软件系统中一个复杂对象的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定

  • 优点
  • 扩展性好,对象每一个属性的构建相互独立,有利于解耦。
  • 建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险
  • 缺点
  • 如果对象建造者发生变化,则建造者也要同步修改,后期维护成本较大
  • 一种建造者对应一种类型建造,一个建造者基本很难建造多种类型对象
@Data
class Product {
    private String name;
    private String price;
    // Product 的建造者  Builder
    public static class Builder{
        public static Builder builder(){
            Builder builder = Builder();
        }
        private Product product = new Product();
        public Builder name(String name){ product.name = name; return this;}
        public Builder price(String price){ product.price = price; return this; }
        //返回产品对象
        public Product build() { return product; }
    }
}

结构型

适配器模式

连通上下游功能。一般是现有的功能和产品要求的接口不兼容,需要做转换适配。平时见到的 PO,BO,VO,DTO 模型对象之间的相互转换也是一种适配的过程

  • 优点:提高了类的复用,灵活性好
  • 缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现
//类的适配器模式
public class Source {
    public void sayHello() {
        System.out.println("lwl:hello!");
    }
}
public interface Targetable {
    /* Source方法相同 */
    public void sayHello();
    /* 新增的方法 */
    public void hi();
}
// Source 用 Adapter 适配成 Targetable
public class Adapter extends Source implements Targetable {
    @Override
    public void hi() {
        System.out.println("csc:hi!");
    }
}
//对象的适配器模式
public class Source {
    public void sayHello() {
        System.out.println("lwl:hello!");
    }
}
public interface Targetable {
    /* Source方法相同 */
    public void sayHello();
    /* 新增的方法 */
    public void hi();
}
//  Source的对象适配成 Targetable
public class Adapter implements Targetable {
    private Source source;
    public Adapter(Source source){ this.source = source; }
    public void sayHello(){ source.sayHello(); }
    @Override
    public void hi() {
        System.out.println("csc:hi!");
    }
}

装饰器模式

增强对象功能,动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删)

public interface Show(){ public void acting(); }
public class Artist implements Show {
    public void acting(){
        System.out.println("lwl 在唱歌!");
    }
}
public class DecoratorArtist implements Show{
    Artist artist;
    DecoratorArt(Artist artist){
        this.artist = artist;
    }
    public void acting(){
        System.out.println("lwl 在弹钢琴!"); //增强的功能
        this.artist.acting();
        System.out.println("表演完毕!"); //增强的功能
    }
}

代理模式

代理类是客户类和委托类的中介,可以通过给代理类增加额外的功能来扩展委托类的功能,这样只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则

  • 和装饰器模式的区别:代理模式着重于增强类功能,且对面屏蔽原对象的创建过程;装饰器模式增强的是对象,且装饰器模式有一个动态传递原对象的步骤
  • 和对象的适配器模式优点像:不过代理模式着重的是对原功能增强,适配器模式着重的是对新功能的兼容
  • 优点-1、职责清晰。 2、高扩展性
public class Artist implements Show {
    public void acting(){
        System.out.println("lwl 在唱歌!");
    }
}
public class ProxyArtist implements Show{
    Artist artist;
    ProxyArtist(){
        this.artist = new Artist();//屏蔽了 artist 对象的创建
    }
    public void acting(){
        System.out.println("lwl 在弹钢琴!"); //增强的功能
        this.artist.acting();
        System.out.println("表演完毕!"); //增强的功能
    }
}
public class Demo {
    public static void main(String[] arg){
        Show show = new ProxyArtist();
        show.acting();
    }
}

桥接模式

桥接模式侧重于功能的抽象,从而基于这些抽象接口构建上层功能。一般的java 项目都会将接口和实现分离原因,就是基于桥接模式。提高了系统的扩展能力,当引用的底层逻辑有不同的设计实现时,继承抽象接口重新实现一套即可,旧的不变,符合代码设计的开闭原则

  • jdbc 的驱动:常用的JDBC 和 DriverManager,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接
  • Unix 的文件系统:VFS(virtual File System)使得 Unix 系统可以在不同物理介质上的不同文件系统进行读写
public interface FileSystem(){
  public void open(int file);
  public String loading(int file);
  public void store(int file, String data);
}
//网络上的文件系统
public class NetFileSystem implements FileSystem {
  public void open(int file){ System.out.println(" netfile opening...."); }
  public String loading(int file) {System.out.println(" net loading ...."); }
  public void store(int file, String data) {System.out.println(" send to network ...."); }
}
//磁盘文件系统
public class DiskFileSystem implements FileSystem{
  public void open(int file){ System.out.println(" disk opening...."); }
  public String loading(int file) {System.out.println(" disk loading ...."); }
  public void store(int file, String data) {System.out.println(" write back disk ...."); }
}
public class Linux {
    FileSystem fileSystem;
    //底层功能提供接口,桥接模式:功能和具体实现分离
    //可以桥接 NetFileSystem 或者 DiskFileSystem 作为文件系统
    public void set(FileSystem fileSystem){ this.fileSystem = fileSystem; }
    //上层功能读数据
    public String read(int file){
        fileSystem.open(file);
        ... // Linux 自己的系统功能
        fileSystem.loading(file);
        ...

    }
    //上层功能写数据
    public String write(int file, String data){
        fileSystem.open(file);
        ....

        fileSystem.store(file,data);
    }
}
  • 可配合适配器模式使用

享元模式

多个对象共享某些属性。在创建有大量对象时,可能会造成内存溢出,把其中共同的部分抽象出来,如果有相同的请求,直接返回在内存中同一份属性,避免重新创建

  • 如 jdbc 连接池的连接对象,它们会共享池对象的 url、driverClassName、username、password 等属性
public class ConnectionPool {
    private Vector pool;
    /*公有属性*/
    private String url = "jdbc:mysql://localhost:3306/test";
    private String username = "root";
    private String password = "root";
    private String driverClassName = "com.mysql.jdbc.Driver";
    public ConnectionPool() {
        pool = new Vector(poolSize);
        for (int i = 0; i < poolSize; i++) {
            Class.forName(driverClassName);
                // 每一个 conn 共享了 driverClassName ,url, username, password 等属性
                Connection conn = DriverManager.getConnection(url, username, password);
                pool.add(conn);
            }
    }
    ....

}

外观模式

  • 用多个不同的对象实现一组更复杂的功能。使得类与类之间的关系解耦。如 spring 将使用各个简单的 component、dao 实现复杂的service,就是一种外观模式
  • 功能的组合,组合优于继承
public class DAO {
    public void queryData(){
        System.out.print(" query data ")
    }
}
public class Deal {
    public void dealData(){
        System.out.print(" dealing data ")
    }
}
public class Sender {
    public void send(){
        System.out.print(" send data ")
    }
}
public class Service(){
    private DAO dao;  
    private Deal deal;  
    private Sender sender;
    //封装 DAO,Deal,Sender 的功能,统一对外提供服务
    public void reponse(){
        dao.queryData();
        deal.dealData();
        sender.send();
    }
}

行为型

策略模式

策略模式侧重于不同的场景使用不同的策略。在有多种算法相似的情况下,解决 if…else 所带来的复杂和难以维护

  • 和桥接模式的区别:而桥接模式是结构型模式,侧重于分离底层功能的抽象和实现,底层只有一种实现也可以
// 上学的策略
abstract class Strategy{
    private static final Map strategyMap = new ConcurrentHashMap<>();
    public Strategy(){
        strategyMap.put(getType(), this);
    }
    public static Strategy routing(int type){
        return strategyMap.get(type);
    }
    abstract int getType();
    abstract void method(); //留待子类实现差异
}
//跑路去学校
class RunningStrategy extends Strategy{
    int getType() { return 0; }
    void method() { System.out.println(" Run to school "); }
}
//公交去学校
class BusStrategy extends Strategy{
    int getType() { return 1; }
    void method() { System.out.println(" Go to school by bus "); }
}
//飞去学校
class FlyStrategy extends Strategy{
    int getType() { return 2; }
    void method() { System.out.println(" Fly to school "); }
}
class Context{
    //使用不同的策略
    void method(int strategy){
        Strategy.routing(strategy).method();
    }
}

模板方法

和享元模式有一定的相似处,享元模式侧重于属性的共享,而且是结构上的引用,不一定需要继承;而模板方法是共享相同行为,一定有继承行为

  • 区别于策略模式是它有能抽象出来的共同行为,每一个子类再实现有差异细节
abstract class AbstractHandler{
    // handle是抽象出来的共同逻辑
    void handle(String data){
        System.out.println("通用逻辑1...");
        stepOne(data);
        System.out.println("通用逻辑2...");
        stepTwo(data);
        System.out.println("通用逻辑3...");
    }
    abstract void stepOne(String data); //留待子类实现差异
    abstract void stepTwo(String data); //留待子类实现差异
}
class HelloHandler extends AbstractHandler{
    @Override
    void stepOne(String data) {
        System.out.println("hello: "+data);
    }
    @Override
    void stepTwo(String data) {
        System.out.println("hi: "+data);
    }
}

迭代子模式

循环处理多个相同对象,用来遍历集合或者数组

//迭代的抽象接口
public interface Iterator {
    //前移
    public Object previous();
    //后移
    public Object next();
    public boolean hasNext();
}
// 数组的迭代类
public class ArrayIterator implements Iterator {
    private Object[] datas;
    private int cur = 0;
    public ArrayIterator(Object[] datas){
        this.datas = datas;
    }
    public String previous() {
        if(cur > 0){ cur--;}
        return datas[cur];
    }
    public Object next() {
        if(cur < datas.length-1){ cur++;}
        return datas[cur];
    }
    public boolean hasNext() {
        return pos < datas.length-1 ? true :false;
    }
}

责任链模式

负责处理上游的传递下来的对象,并传递给下一个处理者

  • 和迭代子模式的区别,责任链模式是多个hander处理同一个data,且 hander 处理具有顺序性,不用全部 hander 处理,可在某一 hander 中断,也可继续传递
abstract class Handler {
    private Handler next;
    abstract R handle(T data);
    public void setNext(Handler next){ this.next = next; }
    public void loopHandle(T data){
        R result = this.handle(data);
        if(next!=null && result!=null ) { next.loopHandle(result); }
    }
}
//负责问候
class HelloHandler extends Handler {
    Integer handle(String data) {
        System.out.println(data + " hello! ");
        return 10;
    }
}
//负责计数
class CountHandler extends Handler {
    Double handle(Integer data) {
        System.out.println(" it is " + data);
        return 2.0;
    }
}
public class demo{
    public static void main(String[] args){
        HelloHandler hello = new HelloHandler();
        CountHandler count = new CountHandler();
        hello.setNext(count);
        hello.loopHandle("lwl");
    }
}

观察者模式

事件通知: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知

  • 优点:观察者和被观察者是抽象耦合的
  • 缺点
  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
//观察者
public abstract class Observer {
    public abstract void update(T data);
}
// 被观察对象
public class Subject {
    private List> observers = new ArrayList<>();
    private T state;
    public void deal() {
        ....// 逻辑处理
        //如果修改了 state,通知观察者
        if(...) notifyAllObservers();
    }
    //增加一个观察观察
    public void observe(Observer observer) {
        observers.add(observer);
    }
    public void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update(state);
        }
    }
}

状态机模式

不同的状态不同的响应,实现状态之间的转移

  • 和策略模式的区别
  • 状态机模式是策略模式的孪生兄弟。策略模式可以让用户指定更换的策略算法,而状态机模式是状态在满足一定条件下的自动更换,用户无法指定状态,最多只能设置初始状态
  • 状态机模式重点在各状态之间的切换,从而做不同的事情;而策略模式更侧重于根据具体情况选择策略,并不涉及切换
interface State {
    //当前状态进行处理数据,并返回下一个状态
    abstract State action(T data);
}
@Data
class Context{
    private State state;
    public void invoke(T data){
        state != null ? state = state.action(data) : System.out.println(" nothing " + data);
    }
}
// HelloState -> HiState
class HelloState implements State{
    public State action(String data) {
        System.out.println("hello!" + data);
        return new HiState();
    }
}
// HiState -> FineState
class HiState implements State{
    public State action(String data) {
        System.out.println("how are you ?" + data);
        return new FineState();
    }
}
//最后的状态
class FineState implements State{
    public State action(String data) {
        System.out.println("I am  fine!");
        return null;
    }
}
public class demo{
    public static void main(String[] args){
        Context context = new Context<>();
        context.setState(new HelloState());
        context.invoke("lwl");
        context.invoke("lwl");
        context.invoke("lwl");
        context.invoke("lwl");
    }
}

备忘录

记录上一次的状态,方便回滚。很多时候我们是需要记录当前的状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,恢复到原先的状态

  • 缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
@Data
public class Memento {
    private String state;
    public Memento(String state){ this.state = state; }
}
@Data
public class Storage {
    private String value;
    public void storeMemento(){
        return new Memento(value);
    }
    public void restoreMemento(Memento memento){
        this.value = memento.getValue();
    }
    public void action(){ System.out.println(" Storage类逻辑运行 ");}

}
public class MementoPatternDemo {
    public static void main(String[] args) {
        Storage storage = new Storage();
        storage.setValue(1);
        storage.storeMemento();//备忘,一下
        storage.action();//....逻辑运行
        restoreMemento(Memento memento);//使用备忘录的恢复状态
    }
}

Original: https://www.cnblogs.com/cscw/p/15498485.html
Author: 潜行前行
Title: 技能篇:实际开发常用设计模式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/571972/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Java8-Stream流

    Java8-Stream基础操作 JAVA技术交流群:737698533 在学习Stream之前必须有Lambda,的基础 Stream是Java8的新特性,可以进行对集合进行一些…

    Java 2023年6月6日
    076
  • Spring 内部资源视图解析器配置

    Original: https://www.cnblogs.com/icemomo/p/16703592.htmlAuthor: 冰莫莫Title: Spring 内部资源视图解析…

    Java 2023年6月7日
    066
  • Eclipse下的Java反编译插件 查看源代码不再困难

    Eclipse下的Java反编译插件:Eclipse Class Decompiler,整合了目前最好的2个Java反编译工具Jad和JD-Core,并且和Eclipse Clas…

    Java 2023年5月29日
    088
  • 【校招VIP】[前端][二本][5分]简历的板式比较标准

    关注【校招VIP】 公众号,回复【简历】 ,添加校招顾问微信,即可获取简历指导! 本份简历是一位21届二本前端同学的简历,简历评分6分。 一、学员简历 二、指导意见 简历的版式没有…

    Java 2023年6月5日
    091
  • SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分库分表实践

    在实际业务中,单表数据增长较快,很容易达到数据瓶颈,比如单表百万级别数据量。当数据量继续增长时,数据的 &#x67E5;&#x8BE2;&#x6027;&a…

    Java 2023年6月6日
    070
  • 面向对象ooDay5

    默认的:什么也不写,本类、同包类 说明: java不建议默认访问权限 类的访问权限只能是public或默认的,类中成员的访问权限如上4种都可以 访问权限由小到大依次为:privat…

    Java 2023年6月13日
    068
  • Git命令备忘录

    前言 基本内容 开始之前 基础内容 基础概念 基础命令 远程仓库 分支管理 基本命令 stash命令 rebase命令 冲突问题 标签管理 前言 Git在平时的开发中经常使用,整理…

    Java 2023年6月10日
    060
  • 大小端 与本机大小端模式判断 面试被问到

    大小端定义 Java代码判断 思路:利用强制类型转换,获取大范围数的一部分数据,再分析获得的数据 int big = 0xFFFF0000; //0xFFFF0000&#x…

    Java 2023年6月5日
    084
  • Windows IDEA Community 报错

    运行时报错 “CreateProcess error=206,文件名或扩展名太长” 解决方法:https://plugins.gradle.org/plug…

    Java 2023年6月6日
    081
  • Spring Boot:使用Memcached缓存

    综合概述 Memcached是一个自由开源的,高性能,分布式内存对象缓存系统。Memcached基于内存的key-value存储,用来存储小块的任意数据,这些数据可以是数据库调用、…

    Java 2023年5月30日
    079
  • 高德坐标系转wgs(苹果坐标系) java代码

    private static double PI = 3.14159265358979324;public static double[] gcj02ToWgs(double ln…

    Java 2023年5月29日
    093
  • 工具篇:apache-httpClient 和 jdk11-HttpClient的使用

    关注公众号,一起交流,微信搜一搜: 潜行前行 HttpClient (apache) apache HttpClient 是 java项目里 较为常用的组件之一;对接外部服务时,各…

    Java 2023年6月5日
    075
  • spring cloud Eureka server 问题 Spring Cloud java.lang.TypeNotPresentException

    版本: spring-cloud.version : Greenwich.SR2 pom配置: 1 2 xmlns:xsi="http://www.w3.org/2001…

    Java 2023年5月29日
    077
  • Linux命令拾遗-使用blktrace分析io情况

    原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。 简介 一般来说,想检查磁盘I/O情况,可以使用iostat、iotop、sar等,但这些命令只能做一…

    Java 2023年6月7日
    070
  • 打包并部署vue项目到服务器

    步骤一 执行命令npm run build 步骤二 执行完成命令后会看到工程目录生成了一个dist文件夹,这个文件夹就是我们要部署在java项目中的文件夹,把他复制到SSM项目中去…

    Java 2023年6月7日
    064
  • Java连载146-内存泄漏和容器

    内存溢出和内存泄漏的区别 内存溢出,就是我们在内存种分配了一块内存区域,但是当我添加了超出内存的数据的时候,就会导致溢出部分,覆盖了其他的内存,影响到了其他数据.内存溢出容易招致黑…

    Java 2023年6月13日
    069
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球