【设计模式深度剖析】【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.mzph.cn/pingmian/14836.shtml

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

相关文章

2024电工杯数学建模A题Matlab代码+结果表数据教学

2024电工杯A题保姆级分析完整思路代码数据教学 A题题目&#xff1a;园区微电网风光储协调优化配置 以下仅展示部分&#xff0c;完整版看文末的文章 %A_1_1_A % 清除工作区 clear;clc;close all;warning off; %读取参数%正常读取 % P_LOADxlsread(附件1&#xff1a;各园区典…

前端 CSS 经典:SVG 描边动画

1. 原理 使用 css 中的 stroke 属性&#xff0c;用来描述描边的样式&#xff0c;其中重要的属性 stroke-dasharray、stroke-dashoffset。理解了这两个属性的原理&#xff0c;才能理解描边动画实现的原理。 stroke-dasharray&#xff1a;将描边线变成虚线、其中实线和虚线部分…

小程序丨公告栏功能,自动弹出提醒

发布查询时&#xff0c;您是否遇到这样的困扰&#xff1a; 1、查询发布时间未到&#xff0c;学生进入查询主页后发现未发布任何查询&#xff0c;不断咨询原因。 2、有些重要事项需要进入查询主页就进行强提醒&#xff0c;确保人人可见&#xff0c;用户需要反馈“我知道了”才…

【openlayers系统学习】3.4波段数学计算(计算NDVI)

四、波段数学计算&#xff08;计算NDVI&#xff09; 我们已经看到了如何使用 ol/source/GeoTIFF​ 源代码来渲染真彩色和假彩色合成。我们通过将缩放的反射率值直接渲染到红色、绿色或蓝色显示通道中的一个来实现这一点。还可以对来自GeoTIFF&#xff08;或其他数据瓦片源&…

Day48 Javascript详解

Day48 Javascript详解 文章目录 Day48 Javascript详解一、什么是javascript二、javascript特点三、 Javascript的历史四、Javascript vs Java五、JS的基本数据类型六、JS基本数据类型的特殊点七、数组 一、什么是javascript JavaScript是一种高级的、解释型的编程语言&#xf…

cmake编译redis6.0源码总结

1配置clion使用cygwin模拟linux环境&#xff0c;先下载cygwin后配置 2导入源码&#xff0c;配置cmake文件 由于redis是基于Linux上的Makefile&#xff0c;所以Windows上需要配置CMakeLists.txt使用cmake工具编译运行。github上已经有人尝试编写CMakeLists.txt文件&#xff0c…

MCF-Microbial Cell Factories

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、投稿咨询 一、期刊简介 Microbial Cell Factories 是一份开放的同行评审期刊&#xff0c;涵盖了与微生物细胞作为重组蛋白和天然产物的生产者或作为工业兴趣的生物转化的催化剂的开发、使用和研究相关的任何主题…

【学习笔记】Windows GDI绘图(四)矩阵Matrix详解

矩阵Matrix 基于矩阵在GDI绘图的重要性&#xff0c;所以想深入了学习矩阵的相关属性与方法。 先上个本文中所有的函数图例演示吧。 原型&#xff1a; namespace System.Drawing.Drawing2D;public sealed unsafe class Matrix : MarshalByRefObject, IDisposableMatrix类封装…

系统架构师-考试-基础题-错题集锦2

108.总线-全双工、半双工&#xff1a; 109.软件配置管理-产品配置&#xff1a; 产品配置&#xff1a;指一个产品在其生命周期各个阶段所产生的各种形式和各种版本的文档、计算机程序、部件及数据的集合。 注意&#xff1a;选项中的需求规格说明、设计说明等均可归属于文档。 …

Netty学习02----使用多线程优化Selector

背景前置 在单线程环境下&#xff0c;使用一个线程同时绑定多个事件&#xff1a;连接事件、读事件、写事件。不能充分发挥多核CPU的优势&#xff0c;考虑使用多个线程&#xff0c;每个线程专门负责处理不同的事件&#xff0c;如下图所示&#xff1a;一个线程专门负责连接&#…

【ARK Survival Evolved】方舟:生存进化一键使用服务器开服联机教程

1、进入控制面板 2、第一次购买服务器会安装游戏端&#xff0c;大约5分钟左右&#xff0c;如果长时间处于安装状态请联系客服 3、设置游戏端口 方舟生存进化的设置需要三个端口&#xff0c;它们用于游戏端口&#xff08;必须为首选端口&#xff09;&#xff0c;查询端口&#…

uniapp中使用mockjs模拟接口测试总结(swiper轮播图示例)

完整总结下在uni-app中如何使用Mock.js模拟接口测试&#xff0c;这在后台接口未就绪的情况下非常有用。同时也给出个首页swiper轮播图的mock接口使用。网上的文章都不太完整&#xff0c;这里总结下完整的使用示例&#xff0c;同时也支持h5和小程序平台&#xff0c;分享给需要的…

webpack5 splitChunks分割代码

首先明确webpack 自身的打包行为 当splitChunks为false时&#xff0c;此时不启用任何打包设置 可以看到&#xff0c;静态引入全都打到一个chunk里&#xff0c;动态引入会拆分出来一个chunk,这是纯webpack无配置的打包&#xff0c; webpack会给每个模块打上标记 ,如下 { m…

Python使用multiprocessing实现多进程

大家好&#xff0c;当我们工作中涉及到处理大量数据、并行计算或并发任务时&#xff0c;Python的multiprocessing模块是一个强大而实用的工具。通过它&#xff0c;我们可以轻松地利用多核处理器的优势&#xff0c;将任务分配给多个进程并同时执行&#xff0c;从而提高程序的性能…

基于transformers框架实践Bert系列3-单选题

本系列用于Bert模型实践实际场景&#xff0c;分别包括分类器、命名实体识别、选择题、文本摘要等等。&#xff08;关于Bert的结构和详细这里就不做讲解&#xff0c;但了解Bert的基本结构是做实践的基础&#xff0c;因此看本系列之前&#xff0c;最好了解一下transformers和Bert…

【JavaEE】加法计算器与用户登录实战演练

目录 综合练习加法计算器1. 准备工作2. 约定前后端交互接口3. 服务器代码 用户登录1. 准备工作2. 约定前后端交互接口3. 服务器代码4. 调整前端页面代码 综合练习 理解前后端交互过程接⼝传参, 数据返回, 以及⻚⾯展⽰ 加法计算器 需求: 输⼊两个整数, 点击"点击相加&q…

56. UE5 RPG 给敌人添加AI实现跟随玩家

在这一篇里&#xff0c;我们要实现一下敌人的AI&#xff0c;敌人也需要一系列的行为&#xff0c;比如朝向英雄攻击&#xff0c;移动&#xff0c;在满足条件时施放技能。这些敌人的行为可以通过使用UE的内置的AI系统去实现。 在UE里&#xff0c;只要是基于Character类创建的蓝图…

安卓绕过限制直接使用Android/data无需授权,支持安卓14(部分)

大家都知道&#xff0c;安卓每次更新都会给权限划分的更细、收的更紧。   早在安卓11的时候还可以直接通过授权Android/data来实现操作其他软件的目录&#xff0c;没有之前安卓11授权的图了&#xff0c;反正都长一个样&#xff0c;就直接贴新图了。   后面到了安卓12~13的…

信息系统项目管理师0128:输出(8项目整合管理—8.6管理项目知识—8.6.3输出)

点击查看专栏目录 文章目录 8.6.3 输出 8.6.3 输出 经验教训登记册 经验教训登记册可以包含执行情况的类别和详细的描述&#xff0c;还可包括与执行情况相关的影响、建议和行动方案。经验教训登记册可以记录遇到的挑战、问题、意识到的风险和机会以及其他适用的内容。经验教训…

Debezium+Kafka:Oracle 11g 数据实时同步至 DolphinDB 解决方案

随着越来越多用户使用 DolphinDB&#xff0c;各式各样的应用场景对 DolphinDB 的数据接入提出了不同的要求。部分用户需要将 Oracle 11g 的数据实时同步到 DolphinDB 中来&#xff0c;以满足在 DolphinDB 中实时使用数据的需求。本篇教程将介绍使用 Debezium 来实时捕获和发布 …