设计模式-行为型
设计模式-行为型
行为型设计模式主要用于软件运行时复杂的流程控制。包含:模板方法模式、策略模式、命令模式、职责链模式、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式和解释器模式
模板方法模式
在软件设计时,很多时候系统的运行流程都是确定的,在整个流程中,可能只有部分环节的具体实现是有差别的,这时我们就可以使用模板方法模式,其具体定义为:定义一个操作流程中的算法骨架,将部分算法环节的实现延迟到子类中,使子类可以在不改变算法骨架的前提下对特定步骤进行定制。
以职员的工作流程为例:
class Management {func clockIn() {print("上班")}func working() {print("工作")}func clockOut() {print("下班")}func start() {clockIn()working()clockOut()}
}
无论对于任何岗位的职员,这个流程都不变,对于不同的岗位不同的是具体的工作内容,例如添加一位工程师,以模板方法模式设计:
重构后
...
class Engineer: Management {override func working() {print("软件设计")}
}
使用模板方法模式设计后,代码的复用性更强,但是因为子类修改了父类的方法的实现,有悖里氏替换原则,因此在选择时需要根据具体场景进行分析。
策略模式
策略模式核心原则是定义一系列算法,将每个算法独立封装,使用者可以灵活的进行选择替换。
例如现实生活中到某地的出行方式有很多种,可以灵活选择:出租车、公交车、地铁、自行车等,需要根据路程远近和交通状况灵活的选择,这就是一种策略模式。
重构后
protocol Transport {func toDestination()
}
class Taxi: Transport {func toDestination() {print("出租车")}
}
class Bus: Transport {func toDestination() {print("公交车")}
}
class Subway: Transport {func toDestination() {print("地铁")}
}
class Action {var destination: Stringvar transport: Transportinit(destination: String, transport: Transport) {self.destination = destinationself.transport = transport}func go() {self.transport.toDestination()}
}
let action = Action(destination: "北京", transport: Subway())
action.go()
通过策略模式,不同的Action对象调用go方法很容易根据场景实现不同的行为。
命令模式
命令模式的核心是将请求封装为对象,使得请求的发起与执行分开,发起方和执行方通过命令进行交互。
以教务系统为例,
struct Teacher {var name: Stringvar subject: Stringfunc log() {print("\(name) + \(subject)")}
}
class School {var teachers = [Teacher]()func addTeacher(name: String, subject: String) {teachers.append(Teacher(name: name, subject: subject))}func deleteTeacher(name: String) {teachers = teachers.filter {$0.name != name}}func show() {for teacher in teachers {teacher.log()}}
}
let school = School()
school.addTeacher(name: "学伟", subject: "计算机")
school.addTeacher(name: "张三", subject: "体育")
school.addTeacher(name: "李四", subject: "数学")
school.show()
school.deleteTeacher(name: "李四")
school.show()
其中,School
提供了展示所有教师信息的方法,也提供了添加和删除教师的方法,通过这种方式对教师的操作难以维护,可以使用命令模式对其重构,将添加和删除教师、展示所有教师的逻辑都封装成一种命令。
重构后
struct Teacher {var name: Stringvar subject: Stringfunc log() {print("\(name) + \(subject)")}
}
class SchoolCommand {enum ActionType {case addcase deletecase show}var type: ActionTypevar name: String?var subject: String?init(type: ActionType, name: String? = nil, subject: String? = nil) {self.type = typeself.name = nameself.subject = subject}
}
class School {var teachers = [Teacher]()func runCommand(command: SchoolCommand) {switch command.type {case .add:addTeacher(name: command.name!, subject: command.subject!)case .delete:deleteTeacher(name: command.name!)case .show:show()}}private func addTeacher(name: String, subject: String) {teachers.append(Teacher(name: name, subject: subject))}private func deleteTeacher(name: String) {teachers = teachers.filter {$0.name != name}}private func show() {for teacher in teachers {teacher.log()}}
}
let school = School()
school.runCommand(command: SchoolCommand(type: .add, name: "学伟", subject: "计算机"))
school.runCommand(command: SchoolCommand(type: .add, name: "张三", subject: "体育"))
school.runCommand(command: SchoolCommand(type: .add, name: "李四", subject: "数学"))
school.runCommand(command: SchoolCommand(type: .show))
school.runCommand(command: SchoolCommand(type: .delete,name: "李四"))
school.runCommand(command: SchoolCommand(type: .show))
使用命令模式重构后,对于 School
的操作都通过 命令 SchoolCommand
触发,代码扩展性更强,且命令可以作为对象直接被存储、传输、重复和撤销,在某些场景下会非常有用。
职责链模式
一个请求被发出,从低层向高层依次寻找可以处理此请求的对象,直到找到处理者才结束责任链。
重构后
struct Requet {enum Level {case lowcase middlecase high}var level: Level
}
protocol Handler {var nextHandler: Handler? { get }func handlerRequest(request: Requet)func nextHanderDo(request: Requet)
}
extension Handler {func nextHanderDo(request: Requet) {if let nextHandler = nextHandler {nextHandler.handlerRequest(request: request)} else {print("无法处理请求")}}
}
class HighHandler: Handler {var nextHandler: Handler? = nilfunc handlerRequest(request: Requet) {if request.level == .high {print("HighHandler 处理请求")} else {nextHanderDo(request: request)}}
}
class MiddleHandler: Handler {var nextHandler: Handler? = HighHandler()func handlerRequest(request: Requet) {if request.level == .middle {print("MiddleHandler 处理请求")} else {nextHanderDo(request: request)}}
}
class LowHandler: Handler {var nextHandler: Handler? = MiddleHandler()func handlerRequest(request: Requet) {if request.level == .low {print("LowHandler 处理请求")} else {nextHanderDo(request: request)}}
}
class Chain: Handler {var nextHandler: Handler? = LowHandler()func handlerRequest(request: Requet) {nextHandler?.handlerRequest(request: request)}
}
var request = Requet(level: .low)
Chain().handlerRequest(request: request)
request = Requet(level: .middle)
Chain().handlerRequest(request: request)
request = Requet(level: .high)
Chain().handlerRequest(request: request)
外界只需传入指定等级的请求,责任链内部即可根据等级选择相应的处理逻辑。
责任链模式的核心是将请求发送到责任链上,链上的每一个处理者可以根据实际情况决定是否处理此请求,如果不能处理则将请求继续向上发送,直到被某个处理者处理或者没有处理者为止。这种结构可以灵活地向责任链中增加或删除处理者,对于不同种类的请求,发出方只需要将其发送到责任链上,不需要关心具体被哪一个处理者处理。降低了对象间的耦合性,并且使责任的分担更加清晰。
状态模式
状态模式的核心是:当控制一个对象行为的状态转换过于复杂时,把状态处理的逻辑分离出到单独的状态类中。
在软件设计中,对象在不同的情况下会表现出不同的行为,被称为有状态的对象。影响对象行为的属性被称为状态,影响对象行为的属性被称为状态。对有状态的对象进行编程时,使用状态设计模式可以使代码的内聚性更强。
重构后
class StateContent {var currentState: Stateinit(_ currentState: State) {self.currentState = currentState}func changeState(curState: State) {self.currentState = curState}
}
protocol State {func info()func doAction(content: StateContent)
}
class Open: State {func info() {print("开灯")}func doAction(content: StateContent) {content.currentState = Open()}
}
class Close: State {func info() {print("关灯")}func doAction(content: StateContent) {content.currentState = Close()}
}
class LightButton {var stateContent: StateContentinit(state: State) {self.stateContent = StateContent(state)}func change(state: State) {self.stateContent.changeState(curState: state)}func log() {stateContent.currentState.info()}
}
let light = LightButton(state: Close())
light.log()
light.change(state: Open())
light.log()
其中 StateContent
定义了状态的上下文,用来维护当前开关的状态。而 Open
、Close
则是对状态的封装。
观察者模式
观察者模式又被称为发布-订阅模式,在观察者模式中,一个对象发生变化会通知到所有依赖它的对象,依赖它的对象可以根据情况进行自身行为的更改。
在iOS开发中,通知中心和键值监听系统的实现都使用了观察者模式。如下代码通过实现一个简易的通知中心演示观察者模式
重构后
typealias XWNotificationCallback = (XWNotification) -> Void
struct XWNotification {var name: Stringvar data: Stringvar object: AnyObject?func info() {print("name: \(name), data: \(data), object: \(String(describing: object))")}
}
struct XWObsever {var object: AnyObjectvar callback: XWNotificationCallback
}
class XWNotificationCenter {static let shared = XWNotificationCenter()private var observers = Dictionary<String, Array<XWObsever>>()private init() {}func addObserver(name: String, object: AnyObject, callback: @escaping XWNotificationCallback) {let observer = XWObsever(object: object, callback: callback)if var curObserver = observers[name] {curObserver.append(observer)} else {observers[name] = [observer]}}func removeObserver(name: String) {observers.removeValue(forKey: name)}func postNotification(notification: XWNotification) {if let array = observers[notification.name] {var postNotification = notificationfor observer in array {postNotification.object = observer.objectobserver.callback(postNotification)}}}
}
let key = "KEY"
XWNotificationCenter.shared.addObserver(name: key, object: "监听者A" as AnyObject) { noti innoti.info()
}
//XWNotificationCenter.shared.removeObserver(name: key)
XWNotificationCenter.shared.postNotification(notification: XWNotification(name: key, data: "通知内容"))
以上就是一个简易通知中心的实现,当添加了监听之后,一旦通知被发出,回调方法就会立刻执行,对于相同名称的通知,可以添加多个观察者。
中介者模式
中介者模式的核心是将网状的对象交互结构改为星形结构,即所有的对象都与一个中介者进行交互。使用中介者模式可以使原本耦合性很强的对象间的耦合变得松散,提高系统的灵活性和扩展性。
如下代码演示了网状的对象交互结构
class ServerA {func handleClientA() {print("ServerA 处理 ClientA 的请求")}func handleClientB() {print("ServerA 处理 ClientB 的请求")}
}
class ServerB {func handleClientA() {print("ServerB 处理 ClientA 的请求")}func handleClientB() {print("ServerB 处理 ClientB 的请求")}
}
class ClientA {func requestServerA() {ServerA().handleClientA()}func requestServerB() {ServerB().handleClientA()}
}
class ClientB {func requestServerA() {ServerA().handleClientB()}func requestServerB() {ServerB().handleClientB()}
}
let clientA = ClientA()
clientA.requestServerA()
clientA.requestServerB()
let clientB = ClientB()
clientB.requestServerA()
clientB.requestServerB()
如上所述,两个客户端可以分别与服务端进行交互,有时客户端也可以点对点的与另外的客户端进行交互,这样会使系统的结构更加复杂,可以通过中介者模式统一客户端与服务端的交互逻辑
重构后
class ServerA {func handleClientA() {print("ServerA 处理 ClientA 的请求")}func handleClientB() {print("ServerA 处理 ClientB 的请求")}
}
class ServerB {func handleClientA() {print("ServerB 处理 ClientA 的请求")}func handleClientB() {print("ServerB 处理 ClientB 的请求")}
}
class ClientA {}
class ClientB {}
class Mediator {static func handler(client: AnyObject, server: AnyObject) {if client is ClientA {if server is ServerA {ServerA().handleClientA()} else {ServerB().handleClientA()}} else {if server is ServerA {ServerA().handleClientB()} else {ServerB().handleClientB()}}}
}
let clientA = ClientA()
let clientB = ClientB()
let serverA = ServerA()
let serverB = ServerB()
Mediator.handler(client: clientA, server: serverA)
Mediator.handler(client: clientA, server: serverB)
Mediator.handler(client: clientB, server: serverA)
Mediator.handler(client: clientB, server: serverB)
重构后客户端相关类中无须知道服务端具体的实现细节,中介者统一封装了这些逻辑。
迭代器模式
软件设计中,很多对象都是以聚合的方式组成的,或者其内部包含集合类型的数据,在访问对象时,通常需要通过遍历的方式获取到其中的各个元素。这样,如果对象内部组合的方式产生了变化就必须对源码进行修改。
迭代器模式的核心是提供一个对象来访问聚合对象中的一系列数据,不暴露聚合对象内部的具体实现,这样即保证了类的安全性,也将内部的集合遍历逻辑与聚合对象本身进行了分离。
重构后
protocol Iterator {associatedtype ObjectTypevar cursor: Int { get }func next() -> ObjectType?func reset()
}
class School: Iterator {private var teachers = [String]()typealias ObjectType = Stringvar cursor: Int = 0func next() -> String? {if cursor < teachers.count {let teacher = teachers[cursor]cursor += 1return teacher} else {return nil}}func reset() {cursor = 0}func addTeacher(name: String) {teachers.append(name)}
}
let school = School()
school.addTeacher(name: "学伟")
school.addTeacher(name: "小王")
school.addTeacher(name: "乔布斯")
while let teacher = school.next() {print(teacher)
}
print("遍历完成")
外界对 School
内部的数组是不感知的,使用迭代器模式可以很好的对内部实现进行封闭,外部除了通过类中暴露的函数来操作 teachers 数组外,不能直接操作。
在Swift
标准库中,可以直接使用官方迭代器协议 IteratorProtocol
。
访问者模式
当数据的类型固定,但对其访问的操作相对灵活时,可以采用访问者模式对软件系统进行设计。访问者模式的核心是将数据的处理方式从数据结构中分离出来,之后可以方便地对数据的处理方法进行扩展。
举一个现实生活中应用访问者模式的例子:作为一种数据,不同的角色对其访问会有不同的行为表现,对于景区门票这一数据,作为游客需要购买,作为验票员需要验票,这种场景:
重构后
struct Ticket {var name: String
}
protocol Visitor {func visit(ticket: Ticket)
}
class Tourist: Visitor {func visit(ticket: Ticket) {print("游客购买\(ticket.name)")}
}
class Guard: Visitor {func visit(ticket: Ticket) {print("检票员检查了\(ticket.name)")}
}
let ticket = Ticket(name: "公园门票")
let tourist = Tourist()
tourist.visit(ticket: ticket)
let guarder = Guard()
guarder.visit(ticket: ticket)
如上,不同角色对门票的操作分别封装在了独立的类中,这使之后新增行为变得非常容易,例如财务人员对门票价格进行核对等。
备忘录模式
备忘录模式的定义:在不破坏封装性的前提下,对一个对象的状态进行保存,在需要时,可以方便地恢复到原来保存的状态,备忘录模式又被称为快照模式。
从功能上讲,备忘录模式与命令模式有许多相似之处,都是提供了一种恢复状态的机制;不同的是,命令模式是将操作封装成命令,命令可以回滚,备忘录模式则是存储对象某一时刻的状态,可以将状态进行重置。
例如,很多应用都提供了用户自定义偏好设置的功能,偏好设置的保存与重置可以采用备忘录模式实现。
重构后
protocol MementoProtocol {func allKeys() -> [String]func valueForKey(key: String) -> Anyfunc setValue(value: Any, key: String)
}
class Setting: MementoProtocol {var setting1 = falsevar setting2 = falsefunc allKeys() -> [String] {return ["setting1", "setting2"]}func valueForKey(key: String) -> Any {switch key {case "setting1":return setting1case "setting2":return setting2default:return ""}}func setValue(value: Any, key: String) {switch key {case "setting1":setting1 = value as? Bool ?? falsecase "setting2":setting2 = value as? Bool ?? falsedefault:print("key: \(key) 设置错误")}}func show() {print("setting1: \(setting1) ++ setting2: \(setting2)")}
}
class MementoManager {var dictionary = [String: [String: Any]]()func saveState(obj: MementoProtocol, stateName: String) {var dict = [String: Any]()for key in obj.allKeys() {dict[key] = obj.valueForKey(key: key)}dictionary[stateName] = dict}func resetState(obj: MementoProtocol, stateName: String) {if let dict = dictionary[stateName] {for kv in dict {obj.setValue(value: kv.value, key: kv.key)}}}
}
var setting = Setting()
let manager = MementoManager()
setting.setting1 = true
setting.setting2 = true
manager.saveState(obj: setting, stateName: "vip")
setting.setting2 = false
manager.saveState(obj: setting, stateName: "super")
setting.show()
manager.resetState(obj: setting, stateName: "vip")
setting.show()
manager.resetState(obj: setting, stateName: "super")
setting.show()
MementoManager
是一个快照管理类,可以将任何符合 MementoProtocol
协议的对象进行快照保存。一个对象可以保存多个快照,在需要时可以方便地恢复到某个快照。有存档机制的软件可以按照备忘录设计模式的思路实现。
解释器模式
定义一种简洁的语言,通过实现一个解释器来对语言进行解析,从而实现逻辑。
正则表达式和iOS开发中用于自动布局的 VFL(Visual Format Language)是对解释器模式的应用。
例如,在软件中的页面路由跳转可以采用解释器模式进行设计。
重构后
class Interpreter {static func handler(string: String) {let proto = string.components(separatedBy: "://")if let pro = proto.first {print("路由协议: \(pro)")if proto.count > 1, let last = proto.last {let path = last.split(separator: "?", maxSplits: 2, omittingEmptySubsequences: true)if let pathFirst = path.first {print("路由路径: \(pathFirst)")if path.count > 1, let param = path.last {print("路由参数: \(param)")}}}}}
}
Interpreter.handler(string: "http://www.xxx.com?key=value")
此类用于解析某逻辑的设计模式即解释器模式的应用。
总结
- 模板方法模式:定义算法骨架的前提下允许对关键环节的算法实现做修改
- 策略模式:定义一系列方便切换的算法实现
- 命令模式:将操作封装为命令对象
- 责任链模式:通过责任链对请求进行处理,隐藏处理请求的对象细节
- 状态模式:将变化的属性封装为状态对象进行统一管理
- 观察者模式:通过监听的方式处理对象间的交互逻辑
- 中介者模式:通过定义中介者来将网状结构的逻辑改为星状结构
- 迭代器模式:提供一种访问对象内部集合数据的接口
- 访问者模式:将数据的操作与数据本身分离
- 备忘录模式:通过快照的方式存储对象的状态
- 解释器模式:通过编写解释器对自定义的简单语言进行解析,从而实现逻辑
相关文章:

设计模式-行为型
设计模式-行为型 行为型设计模式主要用于软件运行时复杂的流程控制。包含:模板方法模式、策略模式、命令模式、职责链模式、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式和解释器模式 模板方法模式 在软件设计时,很多时候系…...

Salesforce大揭秘!SaaS鼻祖不为人知的那些事!
Salesforce的世界无疑是广阔的。自从创始人Marc Benioff于1999年创立公司以来,Salesforce一直在打破CRM领域的界限,改变销售、营销和技术的格局。 作为全球领先的B2B科技公司之一,Salesforce和硅谷里的其他企业一样,缔造着一个关…...

Oracle——物化视图
文章目录含义物化视图的语法物化视图的创建1、自动刷新的物化事务 ON COMMIT2、非自动刷新的物化视图 ON demand关于手动刷新物化视图的删除资料参考含义 什么是物化视图? 物化视图,通俗点说就是物理化的视图。 什么叫物理化? 将视图以表结构…...

ur3+robotiq 2f 140配置moveit
ur3robotiq 2f 140配置moveit 参考链接1 参考链接2 官方配置movit教程 搭建环境: ubuntu: 20.04 ros: Nonetic sensor: robotiq_ft300 gripper: robotiq_2f_140_gripper UR: UR3 reasense: D435i 通过下面几篇博客配置好了ur3、力传感器、robotiq夹爪…...

LDO 芯片烫手,问题出在哪里?
设计失误的一个电路,该电路是数字电路的电源,为图方便对12V直接通过线性电源芯片降压到5V: 图1:线性电源降压12V转5V 几块电路板打样好后,测试均发现AMS1117-5.0芯片烫手,负载电流100mA多,也满…...

零日漏洞发展格局及防御策略
在过去的一年半中, 在野利用的零日漏洞数量持续飙升 ,这些软件制造商尚不知晓的漏洞正在被国家行为体黑客组织和勒索软件团伙滥用。 今年上半年,Google Project Zero统计了近20个零日漏洞,其中 大部分针对微软、苹果和谷歌构建的…...

RabbitMQ 可用磁盘空间报警
概要当磁盘可用空间低于设定的值(默认50M),将触发警报,并阻塞所有生产者。这目标是为了避免填满整个磁盘,这将导致所有节点上的写入操作失败,并可能导致RabbitMQ停止服务。如何工作为了减少磁盘被填满的风险…...

Web前端学习:二
二一:文字font-size样式 font-size:**px 控制文字大小,可精准控制大小 默认样式medium,中等的 large,大一号 x-large,再大一号 xx-large,再大一号 small,小一号 <!DOCTYPE html…...

【第一章 计算机网络体系结构,标准化工作相关组织,性能指标,分层结构,OSI参考模型】
第一章 计算机网络体系结构,标准化工作相关组织,性能指标,分层结构,OSI参考模型 1.计算机网络: (1)概念: ①计算机网络是将一个分散的、具有独立功能的计算机系统,通过通…...

SpringIOC源码解析
Spring深度学习(一)——IOC的设计理念Spring的核心思想——IOCSpring流程图DEMO编写Spring IoC容器的加载过程实例化化容器:AnnotationConfigApplicationContext实例化建BeanDefinition读取器: AnnotatedBeanDefinitionReaderBean…...

【Jupyter Notebook的简单入门使用】
【Jupyter Notebook的简单入门使用】简单介绍安装与配置简单使用Markdown关闭简单介绍 Jupyter官网 Jupyter Notebook 介绍 简单来讲,它是一个网页应用,可以进行文档编写,甚至运行 py 代码等功能 安装与配置 下载合适版本的 python &#…...

@Component@Import@Bean加载顺序解析
【前言】 我们在使用Spring注入Bean对象时,会使用不同注解,比如Component Service Controller Import Bean等。由于Service Controller 等都可以归为Component,那么Component 和Import 、Bean是何时被加载的,以及他们之间的顺序呢…...

二极管温度补偿电路工作原理分析
众所周知,PN结导通后有一个约为0.6V(指硅材料PN结)的压降,同时PN结还有一个与温度相关的特性:PN结导通后的压降基本不变,但不是不变,PN结两端的压降随温度升高而略有下降,温度愈高其…...

【C语言】多线程之条件竞争
多线程(三)条件竞争并发程序引起的共享内存的问题死锁互斥锁机制生产者消费者模型信号量机制解决:条件竞争 #include<stdio.h> #include<stdlib.h> #include<pthread.h> void* Print(char* str){printf("%s ",s…...

UE NavigationSystem的相关实现
导航数据的构建流程导航数据的收集导航系统中绑定了Actor、Component注册完成以及取消时的委托,通过这些委托把数据及时更新到导航系统的八叉树结构中导航系统的辅助结构DefaultOctreeController、DefaultDirtyAreasController分别承担了空间数据查询和置脏区域重新…...

Java 继承
文章目录1. 继承概述2. 变量的访问特点3. super 关键字4. 构造方法的访问特点5. 成员方法的访问特点6. 方法重写7. 继承案例1. 继承概述 继承是面向对象三大特征之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。 publ…...

Python学习笔记8:异常
异常 一些内置的异常类 类名描述Exception几乎所有的异常类都是从它派生而来的AttributeError引用属性或给它赋值失败时引发OSError操作系统不能执行指定的任务(如打开文件)时引发,有多个子类IndexError使用序列中不存在的索引时引发&#…...

python保留小数函数总结
python保留小数——‘%f’‘%.nf’% x(定义的变量) 例子:a 82.16332 print(%.1f% a) print(%.2f% a) print(%.3f% a) print(%.4f% a) print(%.10f% a)输出结果python保留小数——format()函数Python2.6 开始ÿ…...

狐狸优化算法(Matlab代码实现)
👨🎓个人主页:研学社的博客💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密…...

浏览器自动化框架沦为攻击者的工具
5月27日消息,安全公司Team Cymru的研究人员表示,越来越多的威胁参与者正在使用免费的浏览器自动化框架作为其攻击活动的一部分。 研究人员表示,该框架的技术准入门槛故意保持在较低水平,以创建一个由内容开发者和贡献者组成的活跃…...

SQL必备知识(自用)
数据库基础知识sql和mysql的区别:数据库查询大全(select)1、select 字段名 from 表;2、In查询:用于过滤你所需要查询的内容3、范围查询:between4、模糊查询:like5、查询空值/非空:is…...

BI工具术语表大全:从字母A-Z全面收录
谈到商业智能行业,变革是不可避免的。为了跟上步伐,各种各样的BI 解决方案正在快速迭代更新,以满足企业的数字化需求,那么市场上BI 工具种类繁杂,到底如何选择适合功能全面、满足自己企业运转情况的、合适的BI 工具呢&…...

vue3 + vite + ts 集成mars3d
vue3 vite ts 集成mars3d 文章目录vue3 vite ts 集成mars3d前言一、创建一个vue3 vite ts项目二、引入mars3d相关依赖三、vite.config.ts 相关配置四、 新建DIV容器 创建地图前言 使用mars3d过程中,需要集成mars3d到自己的项目中,mars3d开发教程…...

跨境卖家必看的沃尔玛Walmart商家入驻教程
沃尔玛Walmart作为作为全球连锁超市的鼻祖,有不可比拟的知名度。当沃尔玛从线下延伸到线上后,就成为一个自带IP与流量的线上平台,在全世界都拥有数量庞大的消费者群体。所以龙哥就结合自己注册Walmart的过程给大家详细讲解一下。 Walmart卖家…...

【GANs】什么是饱和损失函数 Non-Saturating LossFunction
Saturating VS Non-Saturating Loss functions in GANs【GANs】什么是饱和损失函数 Non-Saturating LossFunctionSaturating VS Non-Saturating Loss functions in GANs 饱和Loss 普通GAN loss是生成器希望最小化被判断为假的概率。x取值范围是[0,1],所以图中函数…...

USB接口虚拟网卡
1 基本概念 1.1 USB转以太网 - ASIX 4-byte length header before every ethernet packet. - Microchip LAN7800 128x32 bit Descriptor RAM, 32 bits DP_DATA address offset 030h for Descriptor RAM access. - Windows CMD参数格式: route /? -> Linux -h …...

基于SpringBoot的外卖项目的优化
基于SpringBoot的外卖项目的优化1、缓存优化1.1、缓存短信验证码问题分析代码改造1.2、缓存菜品数据实现思路1.3、Spring Cache介绍常用注解CachePutCacheEvictCacheable使用方式1.4、缓存套餐数据实现思路代码改造2、读写分离2.1、主从复制存在的问题介绍配置配置主库--master…...

Ubuntu20.04/22.04 ESP32 命令行开发环境配置
ESP32 芯片系列 ESP32分三个系列 ESP32-S ESP32-S3: Xtensa 32位 LX7 双核 240 MHz, 384KB ROM, 512KB SRAM, QFN7x7, 56-pin, 2.4G Wi-Fi BTESP32-S2: Xtensa 32位 LX7 单核 240 MHz, 128KB ROM, 320KB SRAM, QFN7x7, 56-pin, 2.4G Wi-Fi ESP32-C ESP32-C3: RISC-V 32位 单…...

Kali Linux使用Metasploit生成木马入侵安卓系统
额,这是我最后一篇文章了,周一我们开学了 文章目录前言一、Metasploit是什么?演示环境二、生成可执行木马文件1.生成2.运行命令并生成木马配置参数入侵安卓手机命令1.查看对方手机系统信息查看对方手机安装哪些app文件总结前言 前言…...

数据库复习1
一. 简答题(共1题,100分) 1. (简答题) 存在数据库test,数据库中有如下表: 1.学生表 Student(Sno,Sname,Sage,Ssex) --Sno 学号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别 主键Sno 2.教师表 Teacher(Tno,Tname) --T…...