软件构造 | Design Patterns for Reuse and Maintainability
Design Patterns for Reuse and Maintainability
(面向可复用性和可维护性的设计模式)
Open-Closed Principle (OCP) ——对扩展的开放,对修改已有代码的封
Why reusable design patterns
A design…
…enables flexibility to change (reusability)
…minimizes the introduction of new problems when fixing old ones (maintainability)
…allows the delivery of more functionality after an initial delivery (extensibility).
除了类本身,设计模式更强调多 个类/对象之间的关系和交互过程—比接口/类复用的粒度更大.
1 🍉Creational patterns
Factory Method pattern(工厂方法)
Also known as “Virtual Constructor”
(虚拟构造器)
- 当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体 创建的实例时,用工厂方法。
- 定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个 类的实例化延迟到其子类。
// 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(装饰器模式)
对每一个特性构造子类,通过委派机制增加到对象上
继承与装饰器的区别
- 目的:继承主要用于实现代码重用和扩展功能,而装饰器主要用于在不修改原有类代码的情况下动态地添加新功能或修改其行为。
- 耦合性:继承可能导致较高的耦合性,因为子类与父类紧密相关;而装饰器与原始类之间的耦合度较低,因为它们之间是通过接口或抽象类进行交互的。
- 灵活性:装饰器比继承更灵活,因为它们可以在运行时动态地添加或删除功能,而不需要修改原始类的代码。
- 扩展性:虽然继承也支持扩展功能,但装饰器提供了一种更灵活、更可维护的方式来扩展类的功能。
- 使用场景:当需要在多个类之间共享一些公共功能时,可以使用继承;当需要在不修改原始类代码的情况下动态地添加新功能或修改其行为时,可以使用装饰器。
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()); }
}
运行CoffeeShop
的main
方法,你将看到不同装饰器对咖啡成本和描述的影响。这就是装饰器设计模式的一个具体实现,它允许我们在运行时动态地给对象添加新的责任(比如加奶或加糖),同时保持代码的灵活性和可扩展性。
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.");}
}
策略设计模式定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式使得算法可以独立于使用它的客户端变化。
在给出的代码中,我们可以看到以下组成部分:
- 策略接口 (
PaymentStrategy
):
定义了一个支付策略的公共接口,即pay(int amount)
方法。客户端代码将使用此接口与不同的支付策略交互,而无需知道它们的具体实现。 - 具体策略 (
CreditCardStrategy
和PaypalStrategy
):
这两个类分别实现了PaymentStrategy
接口,提供了不同的支付算法实现。它们分别对应于使用信用卡和PayPal进行支付的逻辑。CreditCardStrategy
类保存了信用卡的相关信息(如持卡人姓名、卡号、CVV和到期日期),但在pay
方法中并没有使用这些信息,只是简单地打印了一条消息。在实际应用中,这些信息可能会被用来与信用卡处理系统交互以完成支付。PaypalStrategy
类保存了PayPal账户的相关信息(如电子邮件和密码),但同样在pay
方法中并没有使用这些信息,只是打印了一条消息。在实际应用中,这些信息可能会被用来与PayPal系统交互以完成支付。
- 客户端代码:
客户端代码通常会包含一个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)
,允许客户端利用这个 迭代器进行显式或隐式的迭代遍历:
迭代器设计模式包含以下几个主要角色:
- 迭代器(Iterator):
- 定义一个访问聚合对象元素但不暴露其内部表示的接口。
- 提供遍历聚合对象的方法,如
hasNext()
(检查是否还有下一个元素)和next()
(获取下一个元素)。
- 具体迭代器(ConcreteIterator):
- 实现迭代器接口,并跟踪遍历中的当前位置。
- 使用聚合对象的内部表示法来遍历元素。
- 聚合(Aggregate):
- 定义创建迭代器对象的接口。
- 通常是包含一组元素的类,如列表、集合等。
- 具体聚合(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
接口,并提供了创建迭代器的方法。MyListIterator
是MyList
的内部类,实现了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
)和两个具体的元素类(Book
和 Fruit
)。这里我会逐一分析并解释代码。
抽象元素接口(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
方法的实现。 - 方法
accept
:Book
类的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设计模式的一部分,但是有几个地方需要注意:
accept
方法的访问级别:在Book
和Fruit
类中,accept
方法应该是public
的,以便它们可以正确地实现ItemElement
接口。ShoppingCartVisitor
接口:虽然您没有提供ShoppingCartVisitor
接口的定义,但根据代码中的使用,我们可以推测它应该定义了一个或多个visit
方法,这些方法接受不同类型的ItemElement
(例如Book
和Fruit
)作为参数。- 返回类型:
accept
方法返回了一个整数。在Visitor模式中,这通常用于表示操作的结果或状态,但在某些情况下,该方法也可能返回void
。 - 方法的实际业务逻辑:通常,在
accept
方法内部不直接执行业务逻辑,而是在visitor
的visit
方法中执行。这样,您就可以在不修改元素类的情况下添加新的操作。
/* 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”
相关文章:

软件构造 | Design Patterns for Reuse and Maintainability
Design Patterns for Reuse and Maintainability (面向可复用性和可维护性的设计模式) Open-Closed Principle (OCP) ——对扩展的开放,对修改已有代码的封 Why reusable design patterns A design… …enables flexibility to change …...

Python数据分析-股票分析和可视化(深证指数)
一、内容简介 股市指数作为衡量股市整体表现的重要工具,不仅反映了市场的即时状态,也提供了经济健康状况的关键信号。在全球经济体系中,股市指数被广泛用于预测经济活动,评估投资环境,以及制定财政和货币政策。在中国…...

Linux如何安装openjdk1.8
文章目录 Centosyum安装jdk和JRE配置全局环境变量验证ubuntu使用APT(适用于Ubuntu 16.04及以上版本)使用PPA(可选,适用于需要特定版本或旧版Ubuntu)Centos yum安装jdk和JRE yum install java-1.8.0-openjdk-devel.x86_64 安装后的目录 配置全局环境变量 vim /etc/pr…...
【LLVM】LTO学习
看这篇文章,文中的代码都是错的,给出的命令行也是错的。 真不如参考文献中也是华为的外国员工写的PPT。 但是,上述的文件中的指令也存在报错,还是官方文档看着舒服。...

事务的特性-原子性(Atomicity)、一致性(Consistency)、隔离性(Asolation)、持久性(Durability)
一、引言 1、数据库管理系统DBMS为保证定义的事务是一个逻辑工作单元,达到引入事务的目的,实现的事务机制要保证事务具有原子性、一致性、隔离性和持久性,事务的这四个特性也统称为事务的ACID特性 2、当事务保持了ACID特性,才能…...

redis哨兵模式(Redis Sentinel)
哨兵模式的背景 当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式。 为了解决单点故障和提高系统的可用性,需要一种自动化的监…...
【牛客】牛客小白月赛97 题解 A - E
文章目录 A - 三角形B - 好数组C - 前缀平方和序列D - 走一个大整数迷宫E - 前缀和前缀最大值 A - 三角形 map存一下每个数出现了多少次,再遍历map #include <bits/stdc.h>using namespace std;#define int long long using i64 long long;typedef pair<…...
Spring Boot中泛型参数的灵活运用:最佳实践与性能优化
泛型是Java中一种强大的特性,它提供了编写通用代码的能力,使得代码更加灵活和可复用。在Spring Boot应用程序中,泛型参数的灵活运用可以带来诸多好处,包括增强代码的可读性、提高系统的健壮性以及优化系统的性能。本文将深入探讨在…...
MySQL建表时的注意事项
以下是我对MySQL建表时的注意事项。其实,建表事项有很多,我的总结如下: 1 存储引擎的选择,一般做开发,都是要支持事务的,所以选择InnoDB 2 对字段类型的选择: 对于日期类型如果要记录时分…...

Advanced RAG 09:『提示词压缩』技术综述
编者按: 如何最大限度地发挥 LLMs 的强大能力,同时还能控制其推理成本?这是当前业界研究的一个热点课题。 针对这一问题,本期精心选取了一篇关于"提示词压缩"(Prompt Compression)技术的综述文章。正如作者所说…...
(13)DroneCAN 适配器节点(二)
文章目录 前言 2 固件 2.1 基于F103 2.2 基于F303 2.3 基于F431 3 ArduPilot固件DroneCAN设置 3.1 f303-通用设置示例 4 DroneCAN适配器节点 前言 这些节点允许现有的 ArduPilot 支持的外围设备作为 DroneCAN 或 MSP 设备适应 CAN 总线。这也允许扩展自动驾驶仪硬件的…...

摸鱼大数据——Spark基础——Spark环境安装——Spark Local[*]搭建
一、虚拟机配置 查看每一台的虚拟机的IP地址和网关地址 查看路径: cat /etc/sysconfig/network-scripts/ifcfg-ens33 2.修改 VMware的网络地址: 使用VMnet8 3.修改windows的对应VMware的网卡地址 4.通过finalshell 或者其他的shell连接工具即可连接使用即可, 连接后, 测试一…...
函数内部结构分层浅析(从MVC分层架构联想)
函数内部结构分层浅析(从MVC分层架构联想) 分层架构:一种将软件代码按不同功能进行划分的架构模式。 优点包括: 可维护性:各层职责明确,易于单独修改维护。 可扩展性:方便添加或修改某一层,不…...

【three.js案例二】时空隧道
import * as THREE from ./build/three.module.js // 引入轨道控制器扩展库OrbitControls.js import { OrbitControls } from three/addons/controls/OrbitControls.js; // 引入dat.gui.js的一个类GUI import { GUI } from three/addons/libs/lil-gui.module.min.js;// 场景 co…...

动手学深度学习(Pytorch版)代码实践 -计算机视觉-48全连接卷积神经网络(FCN)
48全连接卷积神经网络(FCN) 1.构造函数 import torch import torchvision from torch import nn from torch.nn import functional as F import matplotlib.pyplot as plt import liliPytorch as lp from d2l import torch as d2l# 构造模型 pretrained…...

【Python游戏】猫和老鼠
本文收录于 《一起学Python趣味编程》专栏,从零基础开始,分享一些Python编程知识,欢迎关注,谢谢! 文章目录 一、前言二、代码示例三、知识点梳理四、总结一、前言 本文介绍如何使用Python的海龟画图工具turtle,开发猫和老鼠游戏。 什么是Python? Python是由荷兰人吉多范…...
【无标题】c# WEBAPI 读写表到Redis
//c# WEBAPI 读写表到Redis using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using Newtonsoft.Json; using StackExchange.Redis; using System.Data; using System.Web; namespace …...
【剑指Offer系列】53-0到n中缺失的数字(index)
给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。 示例 1: 输入:nums [3,0,1] 输出:2 解释:n 3,因为有 3 个数字,所以所有的数字都在范围 [0,3]…...
docker compose部署zabbix7.0官方方法快速搭建
环境介绍: 系统:centos7 官方文档:https://www.zabbix.com/documentation/current/zh/manual/installation/containers docker镜像加速 vi /etc/docker/daemon.json{"registry-mirrors": ["https://docker.1panel.live&quo…...
分库分表之后如何设计主键ID(分布式ID)?
文章目录 1、数据库的自增序列步长方案2、分表键结合自增序列3、UUID4、雪花算法5、redis的incr方案总结 在进行数据库的分库分表操作后,必然要面临的一个问题就是主键id如何生成,一定是需要一个全局的id来支持,所以分库分表之后,…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...

VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...