【设计模式深度剖析】【2】【结构型】【装饰器模式】| 以去咖啡馆买咖啡为例 | 以穿衣服出门类比

👈️上一篇:代理模式    |   下一篇:适配器模式👉️

目 录

  • 装饰器模式
  • 定义
    • 英文原话
    • 直译
    • 如何理解呢?
    • 4个角色
    • 类图
      • 1. 抽象构件(Component)角色
      • 2. 具体构件(Concrete Component)角色
      • 3. 装饰(Decorator)角色
      • 4. 具体装饰(Concrete Decorator)角色
      • 说明:
    • 代码示例
  • 典型场景
  • 示例解析:以去咖啡馆买咖啡为例
    • 类图
    • 抽象构件
    • 具体构件
    • 装饰器类
    • 测试类:去咖啡馆买咖啡
    • 注意
  • Java I/O类库借鉴了装饰器模式
    • UML类图
    • 准备待读取的文件
    • 代码示例1:读取文件,并打印输出
      • notes1:
      • notes2:
    • 代码示例2:解决乱码问题
      • notes:
  • 适配器模式与装饰器模式的区别
  • 搞懂了?那我们玩点带有迷惑型的东西
    • 为什么说Java io类库借鉴了装饰模式,而不是等同于装饰模式
    • 类比
  • 拓展内容
    • 抽象类相关基础

装饰器模式

装饰器(Decorator)模式:

就是,是不一样的烟火,对进行任何的装饰之后依旧是那个,归来依旧是那个少年!

装饰模式是继承关系的一个替代方案。装饰类Decorator,不管装饰多少层,返回的对象还是原类型(记住加粗的这句话,默念三遍,很重要)。

不管装饰多少次,类型不要发生变化才是装饰器模式。

装饰者的出现也再一次的证明了面向对象的设计原则:多用组合,少用继承!对扩展开放,对修改关闭!

==> 本文示例源码,点击查看 <==

定义

英文原话

Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality.

直译

动态地向对象附加额外的职责,同时保持相同的接口。装饰器为扩展功能提供了一种比子类化更灵活的替代方案。

如何理解呢?

装饰器模式是继承关系的替代:

装饰模式可以替代继承,解决类膨胀的问题。

下面会进行详细的阐述。

4个角色

类图

在这里插入图片描述

1. 抽象构件(Component)角色

该角色用于规范需要装饰的对象(原始对象)。

2. 具体构件(Concrete Component)角色

该角色实现抽象构件接口,定义一个需要装饰的原始类。

3. 装饰(Decorator)角色

该角色持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口。

4. 具体装饰(Concrete Decorator)角色

该角色负责对构件对象进行装饰。

说明:

装饰类和被装饰类可以独立发展,而不会相互耦合。即Component 类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件。

装饰模式是继承关系的一个替代方案。装饰类Decorator,不管装饰多少层,返回的对象还是Component(记住加粗的这句话,默念三遍,很重要)。

装饰模式可以动态地扩展一个实现类的功能

装饰模式的缺点:多层的装饰是比较复杂的。

代码示例

package com.polaris.designpattern.list2.structural.pattern2.decorator.proto;// 抽象组件
interface Component {  void operate();  
}  // 具体组件  
class ConcreteComponent implements Component {  @Override  public void operate() {  System.out.println("执行具体组件的操作");  }  
}  // 抽象装饰器  
class Decorator implements Component {  protected Component component;  public Decorator(Component component) {  this.component = component;  }  @Override  public void operate() {  if (component != null) {  component.operate();  }  }  
}  // 具体装饰器  
class ConcreteDecorator extends Decorator {  public ConcreteDecorator(Component component) {  super(component);  }  @Override  public void operate() {  super.operate(); // 调用抽象装饰器的operation方法  // 添加额外的功能或行为  addedBehavior();  }  public void addedBehavior() {  System.out.println("执行具体装饰器的额外操作");  }  
}  // 客户端代码  
public class ClassicalDecoratorDemo {public static void main(String[] args) {  Component component = new ConcreteComponent();  // 递归地组合装饰器对象  Component decoratedComponent = new ConcreteDecorator(new ConcreteDecorator(component));  // 执行操作  decoratedComponent.operate();  }  
}
/* Output:
执行具体组件的操作
执行具体装饰器的额外操作
执行具体装饰器的额外操作
*///~

典型场景

  1. ■ 需要扩展一个类的功能,或给一个类增加附加功能。
  2. ■ 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  3. ■ 需要为一批类进行改装或加装功能。

NOTES:

  1. 装饰模式是对继承的有力补充。
  2. 单纯使用继承时,在一些情况下就会增加很多子类,而且灵活性差,维护也不容易。
  3. 装饰模式可以替代继承,解决类膨胀的问题,如Java基础类库中的io类库(输入输出流相关的类)大量借鉴了装饰模式(参考进一步分析)。

示例解析:以去咖啡馆买咖啡为例

类图

在这里插入图片描述

抽象构件

对咖啡馆的咖啡进行了抽象

package com.polaris.designpattern.list2.structural.pattern2.decorator;// 咖啡接口
interface Coffee {String getDescription();double getCost();
}

具体构件

咖啡馆的最普通的咖啡,对抽象构件的一种实现。

package com.polaris.designpattern.list2.structural.pattern2.decorator;// 基础咖啡实现
class SimpleCoffee implements Coffee {@Override  public String getDescription() {  return "美式咖啡";  }  @Override  public double getCost() {  return 2.5;  }  
}

装饰器类

抽象出装饰器角色,一个抽象类,实现了抽象构件Coffee接口,

它的实现在这里有两个,一个是加糖装饰器 SugarDecorator,加奶装饰器MilkDecorator ,还可以根据需要继续扩展…

package com.polaris.designpattern.list2.structural.pattern2.decorator;// 咖啡装饰器接口
abstract class CoffeeDecorator implements Coffee {protected Coffee coffee;  public CoffeeDecorator(Coffee coffee) {  this.coffee = coffee;  }  @Override  public String getDescription() {  return coffee.getDescription();  }  @Override  public double getCost() {  return coffee.getCost();  }  
}  // 加糖装饰器  
class SugarDecorator extends CoffeeDecorator {  public SugarDecorator(Coffee coffee) {  super(coffee);  }  @Override  public String getDescription() {  return coffee.getDescription() + ", 加糖";  }  @Override  public double getCost() {  return coffee.getCost() + 0.5;  }  
}  // 加奶装饰器  
class MilkDecorator extends CoffeeDecorator {  public MilkDecorator(Coffee coffee) {  super(coffee);  }  @Override  public String getDescription() {  return coffee.getDescription() + ", 加奶";  }  @Override  public double getCost() {  return coffee.getCost() + 1.0;  }  
}  // ... 可以添加更多装饰器,如肉桂粉装饰器等

测试类:去咖啡馆买咖啡

package com.polaris.designpattern.list2.structural.pattern2.decorator;public class CoffeeShop {public static void main(String[] args) {  // 创建基础咖啡  Coffee coffee = new SimpleCoffee();System.out.println("你点了:" + coffee.getDescription() + ",价格是:" + coffee.getCost());  // 添加加糖装饰器  Coffee sweetCoffee = new SugarDecorator(coffee);System.out.println("你加了糖,现在是:" + sweetCoffee.getDescription() + ",价格是:" + sweetCoffee.getCost());  // 递归添加加奶装饰器(加在已经加糖的咖啡上)  Coffee latte = new MilkDecorator(sweetCoffee);System.out.println("你又加了奶,现在是:" + latte.getDescription() + ",价格是:" + latte.getCost());  // 你可以继续递归添加更多装饰器...  }  
}/* Output:
你点了:美式咖啡,价格是:2.5
你加了糖,现在是:美式咖啡, 加糖,价格是:3.0
你又加了奶,现在是:美式咖啡, 加糖, 加奶,价格是:4.0
*///~

注意

从上面示例你也会发现,无论装饰几层,类型不变,都是Coffee类型

Coffee coffee = new SimpleCoffee();Coffee sweetCoffee = new SugarDecorator(coffee);Coffee latte = new MilkDecorator(sweetCoffee);

Java I/O类库借鉴了装饰器模式

UML类图

在这里插入图片描述

准备待读取的文件

ExampleFileForRead.txt

相对于当前工程的目录路径是:

src/main/java/com/polaris/designpattern/list2/structural/pattern2/decorator/inputstream/ExampleFileForRead.txt

文件内容如下:

设计模式类型:
1. ■ 创建型(creational)👈️
2. ■ 结构型(structural)👈️
3. ■ 行为型(behavioral)👈️

代码示例1:读取文件,并打印输出

进一步深究

控制台打印输出的中文、特殊字符乱码了

package com.polaris.designpattern.list2.structural.pattern2.decorator.inputstream;import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;/*** 存在打印中文、特殊字符乱码问题*/
public class DecoratorPatternDemo {public static void main(String[] args) {String exampleFile = System.getProperty("user.dir") +File.separator +"src/main/java/com/polaris/designpattern/list2/structural/pattern2/decorator/inputstreamapi" +"/ExampleFileForRead.txt";try (// 创建一个 FileInputStream 对象,用于从文件中读取数据FileInputStream fis = new FileInputStream(exampleFile);// 使用 BufferedInputStream 包装 FileInputStream,添加缓冲功能BufferedInputStream bis = new BufferedInputStream(fis);/*行不通:FilterInputStream构造函数受保护FileInputStream fis = new FileInputStream(exampleFile);FilterInputStream filteris= new FilterInputStream(fis);BufferedInputStream bis = new BufferedInputStream(filteris);*/) {  // 读取并处理数据,这里只是简单地打印到控制台  int data;  while ((data = bis.read()) != -1) {  System.out.print((char) data);  }  } catch (IOException e) {e.printStackTrace();  }  }  
}/* Output:
设计模式类型:
1. ■ 创建型(creational)👈️
2. ■ 结构型(structural)👈️
3. ■ 行为型(behavioral)👈️
*///~

notes1:

上面是一个简单的测试类示例,展示了如何使用 BufferedInputStreamFileInputStream 类似装饰模式的方式。

BufferedInputStreamFilterInputStreamInputStreamFileInputStream 这些类在 Java 的 I/O 框架中与装饰模式(Decorator Pattern)有关,但并不严格地完全遵循装饰模式的经典定义。然而,我们可以说它们的设计受到装饰模式的启发(详细分析见进一步分析),特别是 FilterInputStream 和它的子类 BufferedInputStream 展示了类似装饰模式的行为。

装饰模式允许向一个对象动态地添加职责(即功能),同时保持相同的接口

在 Java I/O 中,InputStream 是所有输入流的基类,定义了一个读取字节的通用接口。

FilterInputStreamInputStream 的一个子类,用于包装另一个 InputStream 并向其添加额外的功能,而不改变其基本的读取接口。

BufferedInputStreamFilterInputStream 的一个子类,它提供了一个带有缓冲区的输入流,用于提高读取性能。它通过包装另一个 InputStream(可能是 FileInputStream 或其他任何 InputStream 的子类)来添加缓冲功能。

notes2:

在这个示例中,FileInputStream 是原始的数据源,而 BufferedInputStream 则作为一个装饰器,为原始数据源添加了缓冲功能。这种关系与装饰模式中的组件(Component)和装饰器(Decorator)之间的关系相似。 FilterInputStream 和它的子类,通过组合(即包装另一个 InputStream 对象)来扩展功能。

可以从源码看出,FilterInputStream 持有一个InputStream的实例,通过构造函数进行的初始化,即FilterInputStream 和子类组合了一个InputStream 对象,

public class FilterInputStream extends InputStream {
/*** The input stream to be filtered.*/protected volatile InputStream in;protected FilterInputStream(InputStream in) {this.in = in;}//... 省略其余代码 ....
}

总的来说,虽然 BufferedInputStreamFilterInputStreamInputStreamFileInputStream 的设计受到装饰模式的启发,但它们并不完全遵循装饰模式的经典定义。不过,这种设计确实展示了如何动态地向对象添加功能而不改变其接口的思想。

代码示例2:解决乱码问题

进一步深究

package com.polaris.designpattern.list2.structural.pattern2.decorator.inputstream;import java.io.*;
import java.nio.charset.StandardCharsets;public class DecoratorPatternDemoOk {public static void main(String[] args) {String exampleFile = System.getProperty("user.dir") +File.separator +"src/main/java/com/polaris/designpattern/list2/structural/pattern2/decorator/inputstream" +"/ExampleFileForRead.txt";try (// 创建一个 FileInputStream 对象,用于从文件中读取数据FileInputStream fis = new FileInputStream(exampleFile);// 使用 InputStreamReader 读取字符,并指定编码为 UTF-8Reader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);// 使用 BufferedReader 包装 InputStreamReader,添加缓冲功能BufferedReader br = new BufferedReader(isr);) {// 读取并处理数据,这里只是简单地打印到控制台  String line;  while ((line = br.readLine()) != null) {  System.out.println(line);  }  } catch (IOException e) {e.printStackTrace();  }  }  
}/* Output:
设计模式类型:
1. ■ 创建型(creational)👈️
2. ■ 结构型(structural)👈️
3. ■ 行为型(behavioral)👈️
*///~

notes:

遇到的乱码问题是因为尝试将文件的字节直接转换为字符((char) data)进行打印,

但是文件的编码(UTF-8)与 Java 默认使用的字符编码(通常是系统默认的,可能不是 UTF-8)不匹配。

在 Java 中,FileInputStream 读取的是原始字节,需要指定正确的字符编码来将这些字节转换为字符串。

为了正确地读取和显示文件内容,应该使用 InputStreamReaderBufferedReader,并指定文件的字符编码,在这个示例,它使用 UTF-8 编码来读取和打印文件内容。

示例中,使用了 InputStreamReader 来读取字符,并指定了编码为 “UTF-8”。

然后,使用BufferedReader包装了 InputStreamReader来提供按行读取的功能

最后,我使用 readLine() 方法读取每一行,并直接打印到控制台。

能够正确地看到文件中的内容,而不会出现乱码。

适配器模式与装饰器模式的区别

装饰器与适配器模式都有一个别名就是包装模式(Wrapper),它们的作用看似都是起到包装一个类或对象的作用,但是使用它们的目的是很不一样的。

  1. 适配器模式的意义是要将一个接口转变成另外一个接口,它的目的是通过改变接口来达到重复使用的目的;(参考:适配器模式)
  2. 而装饰器模式不是要改变被装饰对象的接口,而恰恰要保持原有的接口,但是增强原有对象的功能,或者改变原有对象的处理方法而提升性能。所以这两个模式设计的目的是不同的。

搞懂了?那我们玩点带有迷惑型的东西

为什么说Java io类库借鉴了装饰模式,而不是等同于装饰模式

我们之前说Java I/O包下的类库借鉴了装饰器模式

但是它并不完全等同于经典的装饰器模式

装饰器模式通常允许递归地组合装饰器对象,而Java I/O中的FilterInputStream通常只包装一个InputStream对象。

如果我们区分这一差别,他们实际上是不同的;

严格意义上装饰器模式是可以对一个对象进行多次装饰,之后依旧是原类型,即允许递归的组合装饰器对象。

而java io包中类库却不是多次装饰。

以下进一步分析,下图是之前的类图,如果有必要可以回到之前的代码示例

  • 返回代码示例1

  • 返回代码示例2

在这里插入图片描述

那java io类库不是使用的装饰器模式么?大家不都说java io类库使用的是装饰器模式么?

其实准确的说java io类库借鉴了装饰器模式比较合理些

从上面的类图或者回到之前代码示例1,FilterInputStream的构造方法是protected修饰的,我们无法直接通过构造函数构造他的实例,因此直接使用它的子类构造,如这里的BufferedInputStream

通过传入文件路径字符串构造了FileInputStream对象,通过该FileInputStream实例构造了一个BufferedInputStream实例,BufferedInputStream可以看作是对该InputStream装饰了一层,即增加缓冲功能,就完事了,没有后续的装饰了;

而严格意义上的装饰器模式呢,是允许装饰多层的,装饰完毕依旧是原始的类型。

如果我们区分这一差别,即允许递归地组合装饰器对象(装饰完之后还是之前的类型才对,不能改变类型,不是去构造一个新的类型的实例),java io类库就不符合装饰器模式的要求

也就是如果按照严格意义上来说,对InputStream对象装饰多次,最终依旧是InputStream对象,才算是装饰器模式。

显然这里不是,这里经过了String =1=> InputStream(FileInputStream) =2=> InputStream(BufferedInputStream)第1步之后就不再是String类型了。只是这里形式上看上去和递归地组合装饰器对象非常相像,new BufferedInputStream(new FileInputStream("path_of_file"))

因此这里说是借鉴装饰器模式,且借鉴装饰器模式的部分只就是InputStream(FileInputStream) => InputStream(BufferedInputStream)这个过程。

略微延伸一点,我们打个比方,

java io类库由扩展了,增加了一个XxxInputStream类,以实现对InputStream增加Xxx功能,它需要传入InputStream类型实例来构造,那么它可能的调用方式是这样的:

InputStream fis = new FileInputStream("path_of_file");
XxxInputStream xis = new XxxInputStream(new BufferedInputStream(fis));

它允许递归地组合装饰器对象,这样他就是严格的装饰器模式了。

如果你说,上面代码示例2,形式上很像递归的组合装饰器对象呐,

InputStream fis = new FileInputStream(exampleFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8));

但是实际不是。

即使解决乱码问题,但是那是又引入了另外的接口Reader,与InputStream不是一回事,构造InputStreamReader的实例,通过传入FileInputStream实例(也就是一个InputStream实例),就转换成了Reader类型,就不再是InputStream类型,然后,通过传入该InputStreamReader,并指定编码格式UTF-8,构造了一个带有缓冲功能的BufferedReader,这里可以看简单看作是BufferedReaderInputStreamReader装饰了一层,即缓冲功能,就完事了,没有后续的装饰了,

类比

你大概也发现了,通过上面的比较较真的分析之后,我们对装饰器模式有了更深的认识,就是不管装饰多少次,类型不要发生变化才是装饰器模式。

并不是说只要像这样调用new C(new B(new A()))形似递归的组合装饰器,就是装饰器模式,只要其中发生了类型变化就不再是严格意义上的装饰器模式了。

可以用穿衣服出门类比:

当前的java io类库中的设计可以视为借鉴了装饰器模式,可以看作只包装一层的装饰器模式,就好比穿外套就出门了;

而严格的讲,**经典的装饰器模式(允许递归地组合装饰器对象)**是说你穿上内裤穿上袜子穿上衬衫穿上西服打上领带,这每一步操作都是对(一个对象,人类的一个实例,即一个人类对象)的装饰

穿上内裤之后是你,

穿上袜子是你,

穿上衬衫是你,

穿上西服是你,

打上领带是你。

就是,是不一样的烟火,对进行任何的装饰之后依旧是那个,归来依旧是那个少年!

拓展内容

抽象类相关基础

这部分内容属于总结,不需要死记硬背,用的多了回过头来看都是很简单的内容。

  1. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类

    这里提一下:不考虑默认方法(使用default关键字定义)和静态方法(使用static关键字定义)情况下,接口中的方法都是抽象方法。

    在Java 8及之前的版本中,接口中只能包含抽象方法和常量(即静态且final的变量)

    public interface MyInterface {  // 抽象方法  void method1();  // Java 8及以后:默认方法  default void method2() {  System.out.println("Default implementation of method2");  }  // Java 8及以后:静态方法  static void method3() {  System.out.println("Static method in interface");  }  
    }
    
  2. 构造方法,类方法(用static修饰的方法)不能声明为抽象方法

  3. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类

  4. 实例化

    1. 抽象类和接口均不能实例化,但是可以指向(引用)具体实现(非抽象子类)
    2. 抽象类虽然自身不可以实例化,但是其子类覆盖了所有的抽象方法后,是可以实例化的,所以抽象类的构造函数适用于给其子类对象进行初始化
  5. 构造器:构造函数是对象的基本,没有构造函数就没有对象。

    1. 若在抽象类中显式的声明了有参构造器子类继承时就必须写一个构造函数来调用父类的有参构造器

    2. 具体子类继承抽象父类,父类的有参构造器子类必须显示调用,即在子类的构造器中显式地super(param_object)调用

    3. 这是因为
      构造函数用于构造对象,但是抽象类不能被实例化,
      一旦抽象类声明了有参构造器,而此构造器不能直接被调用(除非子类显式调用),就不能被客户端将参数直接通过调用该抽象类的有参构造器传参进去,
      这个参数对象只能通过具体子类在构造对象时传入,即子类构造器中必须要显式地调用父类有参构造器super(param_object)

    4. 构造器参数类型可以放大,这样会更通用些,比如你要传入某个实现类的实例,可以将构造器参数类型设置为接口类型,这是多态的一种表现,你可以传入任何属于该类型或该类型的子类型对象。

    5. 如果父类中有无参构造器,在子类中可以不写构造器或显式的调用父类的构造函器,Java会自动调用父类无参构造器。

      以下代码是对抽象类构造器部分解释的编码

      abstract class Parent{public Parent(Param param){}public abstract void m();
      }class Son extends Parent{// param type can be larger.public Son(Param param) {// must invoke manually.super(param);}@Overridepublic void m() {}
      }interface Param{}class ParamTypeA implements Param{}public class Demo {public static void main(String[] args) {Param param = new ParamTypeA();Son son = new Son(param);}
      }
      

👈️上一篇:代理模式    |   下一篇:适配器模式👉️

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【调试笔记-20240526-Linux-在 OpenWrt-23.05 发行版上安装 cpolar】

调试笔记-系列文章目录 调试笔记-20240526-Linux-在 OpenWrt-23.05 发行版上安装 cpolar 文章目录 调试笔记-系列文章目录调试笔记-20240526-Linux-在 OpenWrt-23.05 发行版上安装 cpolar 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调试环境调试目标 二、调试步骤…

【找出第 K 大的异或坐标值】python

4层循环暴力超时 class Solution:def kthLargestValue(self, matrix: List[List[int]], k: int) -> int:nums[]for a in range(len(matrix)):for b in range(len(matrix[0])):num0for i in range(a1):for j in range(b1):num^matrix[i][j]nums.append(num)nums.sort()retu…

部门来了个测试开发,听说是00后,上来一顿操作给我看蒙了...

公司新来了个同事&#xff0c;听说大学是学的广告专业&#xff0c;因为喜欢IT行业就找了个培训班&#xff0c;后来在一家小公司实习半年&#xff0c;现在跳槽来我们公司。来了之后把现有项目的性能优化了一遍&#xff0c;服务器缩减一半&#xff0c;性能反而提升4倍&#xff01…

Docker自定义镜像

镜像 镜像包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建自定义镜像就是把上述文件打包的过程。 镜像结构 入口&#xff08;entrypoint&#xff09;&#xff1a;镜像运行入口&#xff0c;一般是程序的启动脚本和参数 层&#xff08;layer&#xff09;&am…

Tensorflow 2.0 安装过程

第一步&#xff1a;进入国内清华软件网站 anaconda | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirroranaconda 使用帮助 | 镜像站使用帮助 | 清华大学开源软件镜像站&#xff0c;致力于为国内和校内用户提供高质量的开源软件镜像、Linux 镜像源服务&…

怎么在Qt Designer设计的界面上显示Matplotlib的绘图?

首先&#xff0c;利用Qt Designer设计界面。 设计好后保存为ui文件。 接着&#xff0c;将ui文件转为py文件。 我喜欢在python中进行转换&#xff0c;因此把转换命令封装为函数&#xff0c;运行一下即可。 import os # pyuic5 -o output_file.py input_file.ui #通过命令把.ui…

OFDM 802.11a的FPGA实现(二十)使用AXI-Stream FIFO进行跨时钟(含代码)

目录 1.前言 2.AXI-Stream FIFO时序 3.AXI-Stream FIFO配置信息 4.时钟控制模块MMCM 5.ModelSim仿真 6.总结 1.前言 至此&#xff0c;通过前面的文章讲解&#xff0c;对于OFDM 802.11a的发射基带的一个完整的PPDU帧的所有处理已经全部完成&#xff0c;其结构如下图所示&…

老外卖27刀每月的教程已经更新

用了两天半的时间&#xff0c;边学习&#xff0c;边整理了一份老外的视频教程&#xff0c;涉及Facebook&#xff0c;YouTube&#xff0c;tiktok等大的流量平台&#xff0c;有案例&#xff0c;有分析&#xff0c;有如何做。 这个教程是老外讲的&#xff0c;没有什么玄乎的塑造价…

RabbitMQ-默认读、写方式介绍

1、RabbitMQ简介 rabbitmq是一个开源的消息中间件&#xff0c;主要有以下用途&#xff0c;分别是&#xff1a; 应用解耦&#xff1a;通过使用RabbitMQ&#xff0c;不同的应用程序之间可以通过消息进行通信&#xff0c;从而降低应用程序之间的直接依赖性&#xff0c;提高系统的…

微软刚发布的Copilot+PC为什么让Intel和AMD尴尬?2024 AI PC元年——产业布局及前景展望

美国东部时间5月20日在微软位于华盛顿的新园区举行的发布会上&#xff0c;宣布将旗下AI助手Copilot全面融入Windows系统&#xff0c;能够在不调用云数据中心的情况下处理更多人工智能任务。 “将世界作为一个提示词就从Windows系统开始”。微软的新PC将是“CopilotPC”&#xf…

spiderfoot一键扫描IP信息(KALI工具系列九)

目录 1、KALI LINUX简介 2、spiderfoot工具简介 3、在KALI中使用spiderfoot 3.1 目标主机IP&#xff08;win&#xff09; 3.2 KALI的IP 4、命令示例 4.1 web访问 4.2 扫描并进行DNS解析 4.3 全面扫描 5、总结 1、KALI LINUX简介 Kali Linux 是一个功能强大、多才多…

Google发布的CAT3D,在1分钟内,能够从任意数量的真实或生成的图像创建3D场景。

给定任意数量的输入图像&#xff0c;使用以这些图像为条件的多视图扩散模型来生成场景的新视图。生成的视图被输入到强大的 3D 重建管道&#xff0c;生成可以交互渲染的 3D 表示。总处理时间&#xff08;包括视图生成和 3D 重建&#xff09;仅需一分钟。 相关链接 论文&#x…

mysql图形化界面及将mysql注册成后台程序

安装图形化界面版本 右键新建数据库 字符集使用utf8防止以后数据库中存在中文字符导致乱码 将mysql注册成后台程序 cmd进入命令行界面 切换路径到cd /mysql/bin 将mysql注册成后台程序 mysqld.exe --install mysql1 (失败&#xff0c;说明没有权限) 以管理员身份打开成功…

未授权访问:Hadoop 未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、通过REST API命令执行 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好的文章…

Qt 在windows下显示中文

Qt在windows平台上显示中文&#xff0c;简直是一门玄学&#xff0c;经过测试&#xff0c;有如下发现&#xff1a; 1&#xff0c; 环境&#xff1a;Qt 5.15.2 vs2019 64位 win11系统 默认用Qt 创建的文件使用utf-8编码格式&#xff0c;此环境下 中文没有问题 ui->textE…

手写tomcat(Ⅲ)——tomcat动态资源的获取

仿写tomcat的Servlet接口体系 之前写过一篇博客&#xff0c;Tomcat的Servlet-GenericServlet-HttpServlet体系的具体结构&#xff0c;以及Servlet的生命周期 Servlet讲解 想要模仿tomcat获取动态资源&#xff0c;就需要我们自己仿写一个Servlet接口体系 主要包括&#xff1a…

软考-下午题-试题二、三

主要是最后一问的不同解答 1、父图子图平衡 1、员工关系是否存在传递依赖&#xff1f;用100字以内的文字说明理由。2019 2、在职员关系模式中&#xff0c;假设每个职员有多名家属成员&#xff0c;那么职员关系模式存在什么问题&#xff1f; 应如何解决&#xff1f;2020 职员关系…

Science 基于尖峰时序编码的模拟神经触觉系统,可实现动态对象分类

快速处理和有效利用手与物体交互过程中产生的动态触觉信号&#xff08;例如触摸和抓握&#xff09;对于触觉探索和灵巧的物体操作至关重要。将电子皮肤&#xff08;e-skins&#xff09;推进到模仿自然触觉的水平&#xff0c;是恢复截肢者和瘫痪患者丧失的功能的可行解决方案&am…

奥维互动地图奥维彩色图源

彩色图源(不足&#xff0c;更精细放大) 等位线3D模式 中科星 谷歌2024(不足没以前高清)

题解:CF1016E Rest In The Shades

题意 平面上有一个点光源 s s s 并以每秒 1 1 1 单位长度的速度从点 ( a , s y ) (a,sy) (a,sy) 移动到点 ( b , s y ) (b,sy) (b,sy)&#xff0c;其中 s y < 0 sy<0 sy<0&#xff1b;在 x x x 轴正方向上有 n n n 不相交、不接触的挡板&#xff0c;第 i i i …