软件构造 | Design Patterns for Reuse and Maintainability

Design Patterns for Reuse and Maintainability

(面向可复用性和可维护性的设计模式)

Open-Closed Principle (OCP) ——对扩展的开放,对修改已有代码的封

Why reusable design patterns

A design…

  1. …enables flexibility to change (reusability)
  2. …minimizes the introduction of new problems when fixing old ones (maintainability)
  3. …allows the delivery of more functionality after an initial delivery (extensibility).

除了类本身,设计模式更强调多 个类/对象之间的关系和交互过程—比接口/类复用的粒度更大.

1 🍉Creational patterns

Factory Method pattern(工厂方法)

Also known as “Virtual Constructor”(虚拟构造器)

  1. 当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体 创建的实例时,用工厂方法。
  2. 定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个 类的实例化延迟到其子类。

在这里插入图片描述

// Abstract product
public interface Trace{// turn om and off debuggingpublic void setDebug(boolean debug);// write out aa debug messagepublic void debug(String message);// write out an error messagepublic void error(String message);
}// Concrete product 1
public class FileTrace implements Trace{...}// Concrete product 1
public class  SystemTrace implements Trace{...}

工厂方法来创建实例

interface TraceFactory {public Trace getTrace();public Trace getTrace(String type);void otherOperation(){};
}public class Factory1 implements TraceFactory {public Trace getTrace() {return new SystemTrace();}
}// 根据类型决定创建哪个具体产品
public class Factory2 implements TraceFactory {public getTrace(String type) {    // public static getTrace(String type);if(type.equals(“file”)return new FileTrace();else if (type.equals(“system”)return new SystemTrace();}
}Trace log1 = new Factory1().getTrace(); //  TraceFactory2.getTrace(“system”)
log1.setDebug(true);
log1.debug( "entering log" );
Trace log2 = new Factory2().getTrace("system");
log2.setDebug(false);
log2.debug("...");

2 🍈Structural patterns

2.1 Adapter适配器模式

Convert the interface of a class into another interface that clients expect to get.

将某个类/接口转换为client期望的其他形式

A LegacyRectangle 组件的 display() 方法希望接收 接收 "x、y、w、h "参数。但客户希望传递 "左上方 x 和 y "以及 "右下方 ”x 和 y”。这种不协调可以通过添加额外的间接层(即适配器对象)来解决。

在这里插入图片描述

没有适配器

在这里插入图片描述

有适配器

在这里插入图片描述

2.2 Decorator(装饰器模式)

对每一个特性构造子类,通过委派机制增加到对象上

在这里插入图片描述

继承与装饰器的区别

  1. 目的:继承主要用于实现代码重用和扩展功能,而装饰器主要用于在不修改原有类代码的情况下动态地添加新功能或修改其行为。
  2. 耦合性:继承可能导致较高的耦合性,因为子类与父类紧密相关;而装饰器与原始类之间的耦合度较低,因为它们之间是通过接口或抽象类进行交互的。
  3. 灵活性:装饰器比继承更灵活,因为它们可以在运行时动态地添加或删除功能,而不需要修改原始类的代码。
  4. 扩展性:虽然继承也支持扩展功能,但装饰器提供了一种更灵活、更可维护的方式来扩展类的功能。
  5. 使用场景:当需要在多个类之间共享一些公共功能时,可以使用继承;当需要在不修改原始类代码的情况下动态地添加新功能或修改其行为时,可以使用装饰器。

An example

以下是一个使用装饰器设计模式的具体例子,这个例子展示如何为一个简单的咖啡类添加不同的“调料”或“装饰”,比如加奶或加糖。

首先,我们定义一个Coffee接口,它有一个getCost()方法和一个getDescription()方法:

public interface Coffee {  double getCost();  String getDescription();  
}

然后,我们创建一个实现了Coffee接口的简单咖啡类SimpleCoffee

public class SimpleCoffee implements Coffee {  @Override  public double getCost() {  return 2.0;  }  @Override  public String getDescription() {  return "Simple Coffee";  }  
}

接下来,我们定义一个装饰器基类CoffeeDecorator,它也实现了Coffee接口,并持有一个Coffee对象的引用:

public abstract class CoffeeDecorator implements Coffee {  protected Coffee coffee;  public CoffeeDecorator(Coffee coffee) {  this.coffee = coffee;  }  @Override  public double getCost() {  return coffee.getCost();  }  @Override  public String getDescription() {  return coffee.getDescription();  }  
}

现在,我们可以创建具体的装饰器类,比如MilkDecorator(加奶的装饰器)和SugarDecorator(加糖的装饰器):

public class MilkDecorator extends CoffeeDecorator {  public MilkDecorator(Coffee coffee) {  super(coffee);  }  @Override  public double getCost() {  return super.getCost() + 0.5; // 假设加奶增加0.5元  }  @Override  public String getDescription() {  return super.getDescription() + ", Milk";  }  
}  public class SugarDecorator extends CoffeeDecorator {  public SugarDecorator(Coffee coffee) {  super(coffee);  }  @Override  public double getCost() {  return super.getCost() + 0.3; // 假设加糖增加0.3元  }  @Override  public String getDescription() {  return super.getDescription() + ", Sugar";  }  
}

最后,我们创建一个客户端类来测试这些装饰器:

public class CoffeeShop {  public static void main(String[] args) {  Coffee coffee = new SimpleCoffee();  System.out.println("Cost: " + coffee.getCost() + ", Description: " + coffee.getDescription());  coffee = new MilkDecorator(coffee);  System.out.println("Cost: " + coffee.getCost() + ", Description: " + coffee.getDescription());  coffee = new SugarDecorator(coffee);  System.out.println("Cost: " + coffee.getCost() + ", Description: " + coffee.getDescription());  // 也可以先加糖再加奶  Coffee anotherCoffee = new SimpleCoffee();  anotherCoffee = new SugarDecorator(anotherCoffee);  anotherCoffee = new MilkDecorator(anotherCoffee);  System.out.println("Cost: " + anotherCoffee.getCost() + ", Description: " + anotherCoffee.getDescription());  }  
}

运行CoffeeShopmain方法,你将看到不同装饰器对咖啡成本和描述的影响。这就是装饰器设计模式的一个具体实现,它允许我们在运行时动态地给对象添加新的责任(比如加奶或加糖),同时保持代码的灵活性和可扩展性。

3 🍌Behavioral patterns

3.1 Strategy

有多种不同的算法来实现同一个任务,但需要client根据需要 动态切换算法,而不是写死在代码里

在这里插入图片描述

在这里插入图片描述

public interface PaymentStrategy {public void pay(int amount);
}public class CreditCardStrategy implements PaymentStrategy {private String name;private String cardNumber;private String cvv;private String dateOfExpiry;public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate){this.name=nm;this.cardNumber=ccNum;this.cvv=cvv;this.dateOfExpiry=expiryDate;}@Overridepublic void pay(int amount) {System.out.println(amount +" paid with credit card");}
}public class PaypalStrategy implements PaymentStrategy {private String emailId;private String password;public PaypalStrategy(String email, String pwd){this.emailId=email;this.password=pwd;}@Overridepublic void pay(int amount) {System.out.println(amount + " paid using Paypal.");}
}

策略设计模式定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式使得算法可以独立于使用它的客户端变化。

在给出的代码中,我们可以看到以下组成部分:

  1. 策略接口 (PaymentStrategy):
    定义了一个支付策略的公共接口,即pay(int amount)方法。客户端代码将使用此接口与不同的支付策略交互,而无需知道它们的具体实现。
  2. 具体策略 (CreditCardStrategyPaypalStrategy):
    这两个类分别实现了PaymentStrategy接口,提供了不同的支付算法实现。它们分别对应于使用信用卡和PayPal进行支付的逻辑。
    • CreditCardStrategy类保存了信用卡的相关信息(如持卡人姓名、卡号、CVV和到期日期),但在pay方法中并没有使用这些信息,只是简单地打印了一条消息。在实际应用中,这些信息可能会被用来与信用卡处理系统交互以完成支付。
    • PaypalStrategy类保存了PayPal账户的相关信息(如电子邮件和密码),但同样在pay方法中并没有使用这些信息,只是打印了一条消息。在实际应用中,这些信息可能会被用来与PayPal系统交互以完成支付。
  3. 客户端代码
    客户端代码通常会包含一个PaymentStrategy类型的变量,并使用它来执行支付操作。客户端代码可以通过在运行时动态地更改此变量的类型来选择不同的支付策略。

下面是一个简单的客户端代码示例,展示了如何使用这些策略:

public class PaymentClient {  private PaymentStrategy paymentStrategy;  public PaymentClient(PaymentStrategy strategy) {  this.paymentStrategy = strategy;  }  public void makePayment(int amount) {  paymentStrategy.pay(amount);  }  public static void main(String[] args) {  PaymentClient client = new PaymentClient(new CreditCardStrategy("John Doe", "1234567890123456", "123", "12/25"));  client.makePayment(100); // 100 paid with credit card  client = new PaymentClient(new PaypalStrategy("user@example.com", "password"));  client.makePayment(50);  // 50 paid using Paypal.  }  
}

在这个示例中,PaymentClient类封装了对支付策略的引用,并提供了makePayment方法来执行支付操作。通过向PaymentClient构造函数传入不同的PaymentStrategy对象,我们可以动态地更改其使用的支付策略。

3.2 Template Method

做事情的步骤一样,但具体方法不同;共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现

在这里插入图片描述

Template Method设计模式是一种行为设计模式,它在一个方法中定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

public abstract class OrderProcessTemplate {public boolean isGift;public abstract void doSelect();public abstract void doPayment();public void giftWrap() {System.out.println("Gift wrap done.");
}public abstract void doDelivery();public final void processOrder() {doSelect();doPayment();if (isGift)giftWrap();doDelivery();}
}public class NetOrder extends OrderProcessTemplate {@Overridepublic void doSelect() {}@Overridepublic void doPayment() {}@Overridepublic void doDelivery() {}
}

3.3 Iterator

客户端希望遍历被放入 容器/集合类的一组ADT对象,无需关心容器的具体类型

也就是说,不管对象被放进哪里,都应该提供同样的遍历方式

在这里插入图片描述

Iterable接口:实现该接口的集合对象是可迭代遍历的

public interface Iterable<T> {...Iterator<T> iterator();
}

Iterator接口:迭代器

public interface Iterator<E> {boolean hasNext();E next();void remove();
}

Iterator pattern:让自己的集合类实现Iterable接口,并实现自己的独特Iterator迭代器(hasNext, next, remove),允许客户端利用这个 迭代器进行显式或隐式的迭代遍历:

在这里插入图片描述

迭代器设计模式包含以下几个主要角色:

  1. 迭代器(Iterator):
    • 定义一个访问聚合对象元素但不暴露其内部表示的接口。
    • 提供遍历聚合对象的方法,如hasNext()(检查是否还有下一个元素)和next()(获取下一个元素)。
  2. 具体迭代器(ConcreteIterator):
    • 实现迭代器接口,并跟踪遍历中的当前位置。
    • 使用聚合对象的内部表示法来遍历元素。
  3. 聚合(Aggregate):
    • 定义创建迭代器对象的接口。
    • 通常是包含一组元素的类,如列表、集合等。
  4. 具体聚合(ConcreteAggregate):
    • 实现聚合接口,返回具体迭代器的实例。
    • 在具体聚合中,通常包含存储元素的私有成员变量。

下面是一个简单的Java示例,展示了迭代器设计模式的基本用法:

// 聚合接口  
interface Aggregate {  Iterator createIterator();  
}  // 迭代器接口  
interface Iterator {  boolean hasNext();  Object next();  
}  // 具体聚合(例如一个列表)  
class MyList implements Aggregate {  private List<Object> elements = new ArrayList<>();  // 添加元素的方法  public void add(Object element) {  elements.add(element);  }  // 创建迭代器的方法  @Override  public Iterator createIterator() {  return new MyListIterator(this);  }  // 内部类:具体迭代器  private class MyListIterator implements Iterator {  private int currentIndex = 0;  private MyList list;  public MyListIterator(MyList list) {  this.list = list;  }  @Override  public boolean hasNext() {  return currentIndex < list.elements.size();  }  @Override  public Object next() {  if (!hasNext()) {  throw new NoSuchElementException();  }  return list.elements.get(currentIndex++);  }  }  
}  // 客户端代码  
public class Client {  public static void main(String[] args) {  MyList list = new MyList();  list.add("Element 1");  list.add("Element 2");  list.add("Element 3");  Iterator iterator = list.createIterator();  while (iterator.hasNext()) {  System.out.println(iterator.next());  }  }  
}

显示的迭代

在这个示例中,MyList类实现了Aggregate接口,并提供了创建迭代器的方法。MyListIteratorMyList的内部类,实现了Iterator接口,并用于遍历MyList中的元素。客户端代码通过调用createIterator()方法获取迭代器,并使用迭代器遍历聚合对象中的元素。

An example of Iterator pattern

public class Pair<E> implements Iterable<E> {private final E first, second;public Pair(E f, E s) { first = f; second = s; }public Iterator<E> iterator() {return new PairIterator();}private class PairIterator implements Iterator<E> {private boolean seenFirst = false, seenSecond = false;public boolean hasNext() { return !seenSecond; }public E next() {if (!seenFirst) { seenFirst = true; return first; }if (!seenSecond) { seenSecond = true; return second; }throw new NoSuchElementException();}public void remove() {throw new UnsupportedOperationException();}}
}

在这里插入图片描述

尽管for-each循环在语法上看起来像是隐式迭代,但实际上它仍然依赖于Iterable接口和Iterator的实现来工作。在运行时,Java会调用iterable.iterator()来获取迭代器,并使用迭代器的hasNext()next()方法来遍历元素。因此,虽然从语法上看似隐式,但从实现上看,迭代仍然是显式的。(for-each循环是Java提供的一种语法糖,用于简化对实现了Iterable<E>接口集合的遍历。)

3.4 Visitor

在这里插入图片描述

/* Abstract element interface (visitable) */
public interface ItemElement {public int accept(ShoppingCartVisitor visitor);
}/* Concrete element */
public class Book implements ItemElement{private double price;...int accept(ShoppingCartVisitor visitor) {visitor.visit(this);}
}public class Fruit implements ItemElement{private double weight;...int accept(ShoppingCartVisitor visitor) {visitor.visit(this);}
}

代码展示了Visitor设计模式中的一部分,包括一个抽象元素接口(ItemElement)和两个具体的元素类(BookFruit)。这里我会逐一分析并解释代码。

抽象元素接口ItemElement

/* Abstract element interface (visitable) */  
public interface ItemElement {  public int accept(ShoppingCartVisitor visitor);  
}
  • 接口定义ItemElement 是一个接口,它定义了一个名为 accept 的方法,该方法接受一个类型为 ShoppingCartVisitor 的参数,并返回一个整数。这个接口表示可以被访问(或称为“可访问的”)的元素。
  • 方法 accept:这个方法用于允许一个 ShoppingCartVisitor 对象“访问”当前的元素对象。accept 方法中通常不会包含任何具体的业务逻辑,它只是调用传入的 visitor 对象的 visit 方法,并将当前对象(this)作为参数传递。

具体元素类

Book

/* Concrete element */  
public class Book implements ItemElement{  private double price;  // ... 其他成员变量和方法 ...  int accept(ShoppingCartVisitor visitor) {  visitor.visit(this);  }  
}
  • 类定义Book 类实现了 ItemElement 接口,这意味着它必须提供 accept 方法的实现。
  • 方法 acceptBook 类的 accept 方法按照 ItemElement 接口的定义实现了对 ShoppingCartVisitor 的访问。当 accept 方法被调用时,它会将 this(即当前的 Book 对象)传递给 visitor 对象的 visit 方法。
  • 注意accept 方法应该是 public 的,因为接口中的方法默认是 public 的。但是在您提供的 Book 类的定义中,accept 方法是默认的包级私有访问权限(即没有显式地声明为 public)。这会导致编译错误,因为子类必须实现接口中的所有方法,并且这些方法的访问级别必须与接口中定义的访问级别相匹配。

Fruit

public class Fruit implements ItemElement{  private double weight;  // ... 其他成员变量和方法 ...  int accept(ShoppingCartVisitor visitor) {  visitor.visit(this);  }  
}
  • 类定义Fruit 类同样实现了 ItemElement 接口,并且也需要提供一个 accept 方法的实现。
  • 方法 accept:与 Book 类类似,Fruit 类的 accept 方法也是将当前对象(this)传递给 visitor 对象的 visit 方法。
  • 同样的问题accept 方法也应该是 public 的,否则会导致编译错误。

总结

代码展示了Visitor设计模式的一部分,但是有几个地方需要注意:

  1. accept 方法的访问级别:在 BookFruit 类中,accept 方法应该是 public 的,以便它们可以正确地实现 ItemElement 接口。
  2. ShoppingCartVisitor 接口:虽然您没有提供 ShoppingCartVisitor 接口的定义,但根据代码中的使用,我们可以推测它应该定义了一个或多个 visit 方法,这些方法接受不同类型的 ItemElement(例如 BookFruit)作为参数。
  3. 返回类型accept 方法返回了一个整数。在Visitor模式中,这通常用于表示操作的结果或状态,但在某些情况下,该方法也可能返回 void
  4. 方法的实际业务逻辑:通常,在 accept 方法内部不直接执行业务逻辑,而是在 visitorvisit 方法中执行。这样,您就可以在不修改元素类的情况下添加新的操作。
/* Abstract visitor interface */
public interface ShoppingCartVisitor { int visit(Book book); int visit(Fruit fruit); 
} public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {public int visit(Book book) {int cost=0;if(book.getPrice() > 50){cost = book.getPrice()-5;}else cost = book.getPrice();System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);return cost;}public int visit(Fruit fruit) {int cost = fruit.getPricePerKg()*fruit.getWeight();System.out.println(fruit.getName() + " cost = "+cost);return cost;}
}

4 🫐Commonality and Difference of Design Patterns

4.1 共性样式1

只使用“继承”,不使用“delegation” 核心思路:OCP/DIP 依赖反转,客户端只依赖“抽象”,不能 依赖于“具体” 发生变化时最好是“扩展”而不是“修改”

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2 共性样式2

两棵“继承树”,两个层次的“delegation”

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

Nuxt3 [Vue warn]: Hydration node mismatch:【解决方案】

[Vue warn]: Hydration node mismatch: 水合节点不匹配 Server rendered element contains more child nodes than client vdom. 服务器呈现的元素包含的子节点多于客户端vdom。 这个问题解决起来也很好解决&#xff0c;看这个问题是怎么出来的&#xff0c;看代码&#xff1a;…

暗影精灵8Pro声音没有了,这个方法可以解决,亲测有效!

这个OMEN by HP Gaming Laptop 16-k0xxx Windows 10 Sound Driver Mod &#xff0c;真的解决了我的大问题&#xff01; 如果你的暗影精灵8 Pro酷睿版突然变得哑巴了&#xff0c;扬声器和麦克风都发不出声音&#xff0c;那可能是声卡驱动出了问题。 别担心&#xff0c;我也是个…

eNSP中三层交换机的配置和使用

一、拓扑图 1.新建拓扑图 2.PC端配置 PC1: PC2&#xff1a; 二、基本命令配置 1.S1配置 <Huawei>system-view [Huawei]sysname S1 [S1]vlan 10 //在交换机 S1 上创建 VLAN 10 [S1-vlan10]vlan 20 // 在交换机 S1 上创建 VLAN 20 [S1-vlan20]quit //退出 VLAN 配置…

舆论中心的《黑神话:悟空》:人们总希望,这只猴子能打破些什么

距离《黑神话&#xff1a;悟空》上线还有60天。外界关于游戏的争议有很多&#xff0c;但游戏科学却很少出来回应什么。 6月9日&#xff0c;博主兲虎发文称&#xff0c;《黑神话&#xff1a;悟空》之所以在发布宣传视频后&#xff0c;一直遭受到所谓性别歧视的攻击与污蔑&#…

短视频营销系统小程序源码

开启全新营销时代 &#x1f3a5;一、引言&#xff1a;短视频营销微信小程序&#xff0c;营销新风尚 在数字化时代&#xff0c;短视频以其直观、生动的特点迅速崛起&#xff0c;成为用户获取信息、娱乐消遣的重要渠道。而短视频营销微信小程序则是将短视频与微信营销完美结合&…

解决node: bad option: -V

出现这个问题是由于我们的不当操作造成的&#xff0c;v是需要小写的&#xff0c;看下图 node --version node -v

缓存双写一致性(笔记)

缓存更新方案 旁路缓存模式 这是比较多的 旁路缓存模式&#xff1a;缓存有就返回&#xff0c;没有数据库查询&#xff0c;放入缓存返回。 还有些常用缓存策略 读穿透模式 读穿透和旁路很相似&#xff0c;程序不需要关注从哪里读取数据&#xff0c;它只需要从缓存查询数据。…

从概念到现实:数字孪生技术在智慧充电站的实践

在电动汽车蓬勃发展的今天&#xff0c;充电基础设施的智能化升级成为了推动新能源汽车产业跃进的关键一环。数字孪生技术&#xff0c;作为智能制造和工业4.0的核心&#xff0c;正在逐渐渗透到智慧充电站的每一个角落——从提高能源效率到增强用户体验&#xff0c;为智慧充电站的…

强化学习-RLHF-PPO入门

一、定义 强化学习微调分类RM模型 数据集格式训练流程Reward 模型训练流程(分类模型&#xff0c;积极为1&#xff0c;消极为0) AutoModelForSequenceClassificationReward 模型训练案例PPO模型训练流程PPO模型训练案例 二、实现 强化学习微调分类 RLHF:基于人类反馈对语言模型…

机器学习之数学基础(七)~过拟合(over-fitting)和欠拟合(under-fitting)

目录 1. 过拟合与欠拟合 1.1 Preliminary concept 1.2 过拟合 over-fitting 1.3 欠拟合 under-fitting 1.4 案例解析&#xff1a;黑天鹅 1. 过拟合与欠拟合 1.1 Preliminary concept 误差 经验误差&#xff1a;模型对训练集数据的误差。泛化误差&#xff1a;模型对测试…

RabbitMQ 消息传递

消息何去何从 mandatory和immediate是channel.basicPublish方法中的两个参数&#xff0c;他们都有当消息传递过程中不可达目的地时将消息返回给生产者的功能。RabbitMQ提供的备份交换器可以将未能被交换器路由的消息&#xff08;没有绑定队列或者没有匹配的绑定&#xff09;存…

学习C++第三天——对引用的深入了解

引用 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空 间&#xff0c;它和它引用的变量共用同一块内存空间。 一个变量可以有多个引用&#xff1a; int main() {//一个变量可以有多个引用int a 10;int& b …

OpenAI 收购桌面实时协作公司 Multi;iOS 18 开放 iPhone 镜像测试丨RTE 开发者日报 Vol.231

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

c++ 设计模式 的课本范例(上)

( 0 ) 这里补充面向对象设计的几个原则&#xff1a; 开闭原则OCP &#xff1a; 面向增补开放&#xff0c;面向代码修改关闭。其实反映到代码设计上就是类的继承&#xff0c;通过继承与多态&#xff0c;可以不修改原代码&#xff0c;又增加新的类似的功能。 依赖倒置原则 Depend…

如何从iPhone恢复错误删除的照片

嘿&#xff0c;iPhone 用户&#xff01;作为一名苹果专业人士&#xff0c;我见过相当多的“哎呀&#xff0c;我删除了它&#xff01;”的时刻。今天&#xff0c;我在这里指导您完成从iPhone中恢复那些珍贵的&#xff0c;错误删除的照片的迷宫。坐下来&#xff0c;拿起你的设备&…

分压电路 ADC计算电压 【老板再也不能开除我了 】

经典分压电路 一个电压过来 adc这里的电压等于&#xff1a; 如是12位adc 那么他最大值就是4095 如参考电压是5v 则&#xff1a;5v/4095 实际电压V*(R2/(R1R2)&#xff09;/adc值 转化&#xff1a;实际电压V 5v*(adc值/4095)/(R2/(R1R2)) &#xff1a;老板再也不能 因为不会…

PointCloudLib-滤波模块(Filtering)-直通滤波

使用直通过滤器过滤点云 在本教程中,我们将学习如何沿着 指定维度 – 即,切断位于 或 在给定用户范围之外。 代码 首先,创建一个文件,比方说,在你最喜欢的 编辑器,并将以下内容放入其中:passthrough.cpp #include <iostream> #include <pcl/point_types.h&g…

推荐系统-FM模型

参考&#xff1a;推荐系统&#xff08;三&#xff09;Factorization Machines&#xff08;FM&#xff09;_svmmf-CSDN博客 一句话概括下FM&#xff1a;隐式向量特征交叉----类似embedding的思想 LR 如果利用LR做特征的二阶交叉&#xff0c;有&#xff1a; 但这个公式存在显著…

Open3D 点云的ISS关键点提取

目录 一、概述 1.1原理 1.2应用场景 1.3算法实现步骤 二、代码实现 2.1 完整代码 2.2关键函数 2.3关键点可视化 三、实现效果 3.1原始点云 3.2提取后点云 一、概述 1.1原理 ISS&#xff08;Intrinsic Shape Signatures&#xff09;关键点提取是一种常用于三维点云的…

【LLM-多模态】高效多模态大型语言模型综述

一、结论写在前面 模型规模的庞大及训练和推理成本的高昂&#xff0c;限制了MLLMs在学术界和工业界的广泛应用。因此&#xff0c;研究高效轻量级的MLLMs具有巨大潜力&#xff0c;特别是在边缘计算场景中。 论文深入探讨了高效MLLM文献的领域&#xff0c;提供了一个全面的视角…