软件设计模式(七):适配器、桥梁、命令、原型模式

news2024/11/25 4:00:08

前言

        这是软件设计模式系列的最后一篇文章,对于23种设计模式荔枝梳理了比较重要的几种,剩下的在实际开发中用的也比较少哈哈哈。在这篇文章中荔枝主要梳理软件设计模式中的适配器模式、桥梁模式、命令模式和原型模式的相关知识。希望能帮助到有需要的小伙伴呢~~~


文章目录

前言

一、适配器模式Adapter(Wrapper)

二、桥接模式Bridge

三、命令模式Command

四、原型模式Prototype

总结


一、适配器模式Adapter(Wrapper)

        适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。就类似于我们笔记本电脑的适配器,把交流电变成可供笔记本电脑使用的直流电,在日常开发中我们经常使用的就是这种设计模式,

package com.crj.adapter;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("c:/test.text");
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader br = new BufferedReader(isr);
        String line = br.readLine();
        while (line != null && !line.equals("")) {
            System.out.println(line);
        }
        br.close();
    }
}

        比如在这段demo中我们要读一个txt文件就必须对对象类型进行转换,比如BufferedReader类实例化需要传入一个InputStreamReader实例对象。这其实就相当于转接,对象类型的转换类似于电源转接。 

需要注意的是:常见的Adapt类并不是Adapter,而是为了方便编程给出的接口的抽象实现类,以避免我们继承接口后需要重写所有的接口实现方法。


二、桥接模式Bridge

        桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。我们可以设想一个场景:交通工具有多种类型,交通工具也有多种颜色,若要创建一个拥有红色法拉利的用户对象,我们是选择重写原来的类型呢还是采用一种别的方式同时使用两种交通工具类?想必小伙伴都会采用后者吧,因为如果采用第一种方式的话可能会导致类爆炸的问题。而桥接模式就能比较好的解决这类需求,桥接模式分离抽象与具体,用聚合方式(桥)连接抽象与具体。

我们首先看看下面demo: 

创建一个接口

public interface Draw{
   public void drawCircle(int radius, int x, int y);
}

接口的实现类

public class RedCircle implements Draw{
   @Override
   public void drawCircle(int radius, int x, int y) {
      System.out.println("Drawing Circle[ color: red, radius: "
         + radius +", x: " +x+", "+ y +"]");
   }
}

class GreenCircle implements Draw {
   @Override
   public void drawCircle(int radius, int x, int y) {
      System.out.println("Drawing Circle[ color: green, radius: "
         + radius +", x: " +x+", "+ y +"]");
   }
}

抽象类

public abstract class Shape {
   protected Draw draw;
   protected Shape(Draw draw){
      this.draw = draw;
   }
   public abstract void draw();  
}

抽象类的实现类

public class Circle extends Shape {
   private int x, y, radius;
 
   public Circle(int x, int y, int radius, Draw draw) {
      super(draw);
      this.x = x;  
      this.y = y;  
      this.radius = radius;
   }
 
   public void draw() {
      draw.drawCircle(radius,x,y);
   }
}

main类

public class BridgePattern{
   public static void main(String[] args) {
      Shape redCircle = new Circle(100,100, 10, new RedCircle());
      Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
 
      redCircle.draw();
      greenCircle.draw();
   }
}

        demo中的场景是用于画一个圆,我们创建了一个draw接口以及相应的圆的绘制实现类,同时定义一个抽象类及其相应的子类,子类中定义一个构造方法并在draw()中调用了具体的绘制实现类,在main中我们通过在实例化Circle对象的时候传入具体的圆的参数及其调用的绘制实现类实现不同类型的圆的绘制。


三、命令模式Command

        命令模式是一种属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。这种模式更多地用在事务Transaction处理的场景,常常将请求类和实现类实现解耦。

命令接口 

public interface Order {
   void execute();
}

接口实现类 

public class BuyStock implements Order {
   private Stock abcStock;
 
   public BuyStock(Stock abcStock){
      this.abcStock = abcStock;
   }
 
   public void execute() {
      abcStock.buy();
   }
}

class SellStock implements Order {
   private Stock abcStock;
 
   public SellStock(Stock abcStock){
      this.abcStock = abcStock;
   }
 
   public void execute() {
      abcStock.sell();
   }
}

请求类Stock 

请求类中主要定义的是获得请求对象后实体类对象对应调用的方法。 

public class Stock {
   
   private String name = "ABC";
   private int quantity = 10;
 
   public void buy(){
      System.out.println("Stock [ Name: "+name+", 
         Quantity: " + quantity +" ] bought");
   }
   public void sell(){
      System.out.println("Stock [ Name: "+name+", 
         Quantity: " + quantity +" ] sold");
   }
}

命令调用类 

import java.util.ArrayList;
import java.util.List;
 
public class Broker {
   private List<Order> orderList = new ArrayList<Order>(); 
 
   public void takeOrder(Order order){
      orderList.add(order);      
   }
 
   public void placeOrders(){
      for (Order order : orderList) {
         order.execute();
      }
      orderList.clear();
   }
}

main类 

public class CommandPatternDemo {
   public static void main(String[] args) {
      Stock abcStock = new Stock();
 
      BuyStock buyStockOrder = new BuyStock(abcStock);
      SellStock sellStockOrder = new SellStock(abcStock);
 
      Broker broker = new Broker();
      broker.takeOrder(buyStockOrder);
      broker.takeOrder(sellStockOrder);
 
      broker.placeOrders();
   }
}

        在将请求类和接口实现类进行联系起来的过程中我们需要首先将请求类对象作为构造参数传入接口实现类进行接口实例类对象的实例化,Broker类对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。


四、原型模式Prototype

        原型模式,又称克隆模式,用于创建重复对象的同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一,一般用于一个对象的属性已经确定且需要产生很多相同对象的时候。使用原型模式的时候需要重写Object类下的clone()方法,这是因为Object类下的clone()方法默认是protected的。

需要注意的是:原型模式是Java中自带的,实现原型模式需要实现标记型接口Cloneable,一般会重写clone()方法,如果只是重写clone()方法,而没实现接口,调用时会报异常。

对于克隆模式我们还需要区分深克隆与浅克隆: 

浅克隆

浅克隆指的是对该类对象进行复制,复制后的类的对象的引用指向相同的地址。这时候如果被克隆对象p1.loc发生变更,那么p2.loc也会发生变更。

package com.crj.prototype;

/**
 * 浅克隆
 */

public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        System.out.println(p2.age + " " + p2.score);
        System.out.println(p2.loc);

        System.out.println(p1.loc == p2.loc);
        p1.loc.street = "sh";
        System.out.println(p2.loc);

    }
}

class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Location {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }
}

深克隆 

深克隆就是将引用的对象也会被克隆一份,这时候就需要在loc方法后面加上 .clone() 方法。

package com.crj.prototype;

/**
 * String需要进一步深克隆吗?
 * 不需要!
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        System.out.println(p2.age + " " + p2.score);
        System.out.println(p2.loc);

        System.out.println(p1.loc == p2.loc);
        p1.loc.street = "sh";
        System.out.println(p2.loc);

        p1.loc.street.replace("sh", "sz");
        System.out.println(p2.loc.street);
    }
}

class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person p = (Person)super.clone();
        p.loc = (Location)loc.clone();
        return p;
    }
}

class Location implements Cloneable {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

注意:

        这里的String类型并不需要执行深克隆,这是因为在常量池中,即使我们变更了p1指向的String对象,在常量池中也只是p1指向的String数据对象发生变更,p2指向的对象并不会发生变更。


总结

        终于结束第一节阶段的学习,感觉最近两个月学的东西得找时间好好梳理复习一下。最近的知识今天在鹅场面试中被命中好多,但是自己都讲得不是很清楚哈哈哈。除了复盘知识,技术的深度还是需要继续深入学习了,总之继续努力吧~

今朝已然成为过去,明日依然向往未来!我是荔枝,在技术成长之路上与您相伴~~~

如果博文对您有帮助的话,可以给荔枝一键三连嘿,您的支持和鼓励是荔枝最大的动力!

如果博文内容有误,也欢迎各位大佬在下方评论区批评指正!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1004183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

选择器进阶与表单表格

华子目录 选择器并集选择器后代选择器子代选择器伪类选择器伪元素选择器结构选择器属性选择器相邻选择器 表单&#xff08;form&#xff09;label标签 表格&#xff08;table标签&#xff09; 选择器 下面是我们之前学习过的选择器 *{}&#xff1a;通配符选择器&#xff0c;选…

JDK8新特性--函数式接口--(Consumer的概念理解,模拟练习,企业实战)全流程彻底搞懂

背景&#xff0c;起因是因为在项目开发过程中&#xff0c;发现了一处代码的写法觉得很新奇看不懂&#xff0c;了解后发现是用到了函数式接口的知识。特此学习记录&#xff0c;整体过程梳理在本文。如果你不满足就会写个CURD&#xff0c;业务代码只会new来new去&#xff0c;代码…

Spring 中三种 BeanName 生成器!

无论我们是通过 XML 文件&#xff0c;还是 Java 代码&#xff0c;亦或是包扫描的方式去注册 Bean&#xff0c;都可以不设置 BeanName&#xff0c;而 Spring 均会为之提供默认的 beanName&#xff0c;今天我们就来看看 Spring 中三种处理不同情况的 beanName 生成器。 1. BeanN…

零基础Linux_4(权限和初识操作系统)具体用户分类+rwx+umask+粘滞位

目录 1. 操作系统初识 1.1 操作系统的基本概念 1.2 操作系统的意义 1.3 指令操作的意义 2. shell命令及运行原理 2.1 shell的概念 2.2 shell 的意义 3. Linux权限 3.1 Linux 具体用户的分类 3.2 用户管理 adduser 新用户名(添加普通用户) 用户登陆 - SSH 用户名 u…

视频号挂公众号链接没完全堵死,还能加,最新方法教程,玩私域流量的福音来了

视频号挂公众号链接 视频号挂公众号链接&#xff0c;不限号&#xff0c;不限次数&#xff0c;不需要绑定公众号&#xff0c;不需要10000阅读量 视频号评论区能挂链接&#xff0c;对视频号做公转私的人来说&#xff0c;可以说是大惊喜&#xff0c;对公司来讲放上自己的推广链接…

前缀和实例1 (【模板】前缀和 )

题目&#xff1a; 算法原理&#xff1a; 前缀和算法能快速求出某一个区间内所有元素的和 1 预处理出来一个前缀和数组dp dp[i] dp[i-1]v[i] (v数组由输入的数字组成&#xff09;&#xff0c;即区间[1,i]的所有元素的和区间[1,i-1]所有元素的和v数组中i下标的元素 2 使用前缀…

获取1688店铺详情 API接口(获取卖家昵称、店铺类型、公司信息、店铺标题、店铺主页)

seller_info-获得店铺详情 1688.seller_info进入测试 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,it…

计算机竞赛 大数据商城人流数据分析与可视化 - python 大数据分析

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于大数据的基站数据分析与可视化 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度…

探索移动应用的自动化测试:如何做出明智的工具选择

引言 随着移动应用的日益普及&#xff0c;其在日常生活和工作中的作用也越来越大。为了确保应用的稳定性和用户体验&#xff0c;自动化测试已经成为了开发流程中不可或缺的一部分。本文将深入探讨如何为移动应用选择合适的自动化测试工具&#xff0c;以及这些工具背后的技术原…

【广州华锐互动】云智慧工厂数字孪生:打造高效、灵活的智能制造新模式

随着工业4.0的到来&#xff0c;数字孪生技术逐渐成为实现工业生产智能化升级的关键。云智慧工厂数字孪生利用先进的数字化技术&#xff0c;创建物理实体的虚拟模型&#xff0c;实现对生产过程的实时监控、优化与管理。 云智慧工厂数字孪生是指通过数字孪生技术&#xff0c;构建…

考研英语笔记:程序员是否勤奋就看他的英语好不好

一位大佬朋友圈写道&#xff1a;看程序员是否勤奋就看他的英语好不好&#xff0c;智商高不高就看他算法好不好。 这句话我当时看到了很触动&#xff0c;默默的记在了心底。 对我来说&#xff0c;算法就免了&#xff0c;但学英语我一直在坚持。我不敢说我是优秀的程序员&#xf…

如何在三星手机上截屏?每一款三星手机的每一种方法,包括S23

无论你是将截图作为保存图片、消息或信息的快速方式&#xff0c;还是作为演示像这篇文章这样有用的操作方法的方式&#xff0c;能够截图都会非常有用。 但并不是所有的手机都以相同的方式进行屏幕截图。事实上&#xff0c;并不是所有的三星手机都能做到这一点。例如&#xff0…

MySql安装包配置

电脑重配过多次&#xff0c;此为mysql安装记录贴&#xff0c;方便查阅 从官网下载的安装包进行本地配置 下载地址 解压下载下来的zip压缩包 解压出来的文件中新增配置my.ini文件 [mysqld] # 设置3306端口 port3306 # 设置mysql的安装目录 basedirD:\\software\\package\\M…

神经网络 05(损失函数)

一、损失函数 在深度学习中, 损失函数是用来衡量模型参数的质量的函数, 衡量的方式是比较网络输出和真实输出的差异&#xff0c;损失函数在不同的文献中名称是不一样的&#xff0c;主要有以下几种命名方式&#xff1a; 损失函数 &#xff08;loss function&#xff09; 代价函…

科研小工具|胰岛素敏感性计算公式

简介 胰岛素敏感就是描述胰岛素抵抗的程度。 计算方式 HOMA-IR是用于评价个体胰岛素抵抗水平的指标。计算方法如下&#xff1a; 胰岛素抵抗指数&#xff08;HOMA-IR&#xff09;空腹血糖&#xff08;FPG&#xff0c;mmol/L&#xff09;空腹胰岛素&#xff08;FINS&#xff0…

上海某游戏小厂面试,也扛不住了...

今天分享一位同学面试上海某游戏公司的面经&#xff0c;同学的技术栈是Java后端&#xff0c;虽然不是大厂&#xff0c;但是一面面试也被问了 25 多个问题&#xff0c;时长也接近 1 小时了 面试过程中&#xff0c;也问到了 Linux socket 编程&#xff0c;游戏公司都会对网络协议…

封神台----为了女神小芳

目录 目录 前言 文章框架 1&#xff0c;题目 2&#xff0c;实验前的准备 3&#xff0c;进入传送门 4,使用Sqlmap对网站进行监测 4.1.检测目标地址是否存在注入点 4.2、检测数据库中的库名 4.3、选择需要爆的库开始爆表名 4.3.1,后面内容的一些注意点: 4.3.2,开始进…

esxi下实现ikuai相同的两个网卡,单独路由配置

1.首先安装配置双网卡。 因为esxi主机只接入了一根外网的网线&#xff0c;那么我们这两个网卡都是一样的网卡&#xff0c;具体的到系统里面进行设置。 2.开机安装系统 进入配置界面&#xff0c;此处就不用多说了&#xff0c;可以看我之前的文档&#xff0c;或者网上其他人的安…

中文版Chatbase轻松帮你实现智能回复

在数字时代&#xff0c;信息量可以说是爆炸性增长&#xff0c;很多企业网站都面临着一个共同的问题&#xff1a;如何在繁忙时还能为访客提供及时而有用的回复&#xff1f;那我可以坚定地说AI问答机器人可以做到。很多人都知道使用Chatbase可以创建聊天机器人来即时回答访客的问…

vue2配置环境变量并且nginx运行成功

需求&#xff1a;我在vue项目配置了生产环境和开发环境&#xff0c;之后通过proxy代理的方式把地址转发到真实的服务器地址上用于请求接口&#xff0c;之后把项目打包后上传到nginx上&#xff0c;之后接口报错404&#xff0c;但是本地运行是可以访问的&#xff0c;找了很久终于…