C++ 设计模式----组件协作型模式

news2024/11/25 18:11:33

面向对象设计,为什么?

回答:变化是复用的天敌!面向对象设计最大的优势在于:抵御变化

重新认识面向对象

理解隔离变化

​ 从宏观层面来看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响减为最小

各司其职

​ • 从微观层面来看,面向对象的方式更强调各个类的“责任”

​ • 由于需求变化导致的新增类型不应该影响原来类型的实现——是所谓各负其责

对象是什么?

​ • 从语言实现层面来看,对象封装了代码和数据。

​ • 从规格层面讲,对象是一系列可被使用的公共接口。

​ • 从概念层面讲,对象是某种拥有责任的抽象。

GOF-23 模式分类

从目的来看:

​ • 创建型(Creational)模式:将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体类型实现引来的冲击。

​ • 结构型(Structural)模式:通过类继承或者对象组合获得更灵活的结构,从而应对需求变化为对象的结构带来的冲击。

​ • 行为型(Behavioral)模式:通过类继承或者对象组合来划分类与对象间的职责,从而应对需求变化为多个交互的对象带来的冲击。

从范围来看:

​ • 类模式处理类与子类的静态关系。

​ • 对象模式处理对象间的动态关系。

从封装变化角度对模式分类

在这里插入图片描述

重构获得模式 Refactoring to Patterns

 面向对象设计模式是“好的面向对象设计”,所谓“好的面向对象设计”指是那些可以满足 “应对变化,提高复用”的设计 。

 现代软件设计的特征是**“需求的频繁变化”。设计模式的要点是“寻找变化点,然后在变化点处应用设计模式,从而来更好地应对需求的变化”.“什么时候、什么地点应用设计模式”**比“理解设计模式结构本身”更为重要。

 设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。**敏捷软件开发实践提倡的“Refactoring to Patterns”**是目前普遍公认的最好的使用设计模式的方法。

重构关键技法

 静态 ->  动态

早绑定 ->  晚绑定

​  继承 ->  组合

 编译时依赖 ->  运行时依赖

 紧耦合 ->  松耦合

模板方法模式(c++)

一、组件协作型模式

“组件协作”模式 :现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。

早绑定——————>晚绑定

早绑定:在应用程序中调用库

晚绑定:在库中调用应用程序,模板方法模式就是把主流程放在库里,库的使用者只需要实现子步骤就可以了

典型模式

• Template Method

• Observer / Event

• Strategy

【1】TemplateMethod

**动机(Motivation)**💡:

  • 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因 (比如框架与应用之间的关系)子步骤无法和任务的整体结构同时实现

**问题思考(Consider)**🤔:

  • 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求

对某一项任务,用结构化软件设计流程(使用了早绑定)和面向对象设计软件流程(使用了晚绑定)的区别:

在这里插入图片描述

(1)结构化软件设计流程

在这里插入图片描述
C++执行代码:

template1_lib.cpp

#include<iostream>
using namespace std;

//程序库开发人员
class Library {
public:
	void Step1() {
		cout << "Step1" << endl;
	}

	void Step3() {
		cout << "Step3" << endl;
	}

	void Step5() {
		cout << "Step5" << endl;
	}
};

template1_app.cpp

#include "template1_lib.cpp"

//应用程序开发人员
class Application {
public:
	bool Step2() {
		cout << "myStep2" << endl;
		return true;
	}

	void Step4() {
		cout << "myStep4" << endl;
	}
};

int main() {
	Library lib;
	Application app;
	lib.Step1();

	if (app.Step2()) {
		lib.Step3();
	}

	for (int i = 0; i < 4; i++) {
		app.Step4();
	}

	lib.Step5();
}

在这里插入图片描述

(2)面向对象软件设计流程

在这里插入图片描述

C++执行代码:

template2_lib.cpp

#include<iostream>
using namespace std;

//程序库开发人员
class Library {
public:
	//稳定 template method
	void Run() {
		Step1();
		if (Step2()) {
			//支持变化 ==> 虚函数的多态调用
			Step3();
		}

		for (int i = 0; i < 4;i++) {
			Step4();//支持变化 ==> 虚函数的多态调用
		}
		Step5();
	}

	virtual ~Library() {};
protected:
	void Step1() {
		//稳定
		cout << "Step1" << endl;
	}

	void Step3() {
		//稳定
		cout << "Step3" << endl;
	}

	void Step5() {
		//稳定
		cout << "Step5" << endl;
	}

	virtual bool Step2() = 0;//变化
	virtual void Step4() = 0;//变化
};

template2_app.cpp

#include "template2_lib.cpp"
#include <iostream>

using namespace std;

//应用程序开发人员
class Application : public Library {
protected:
	virtual bool Step2() {
		//... 子类重写实现
		cout << "override Step2" << endl;
		return true;
	}

	virtual void Step4() {
		//... 子类重写实现
		cout << "override Step4" << endl;
	}
};

int main() {
	Library* pLib = new Application();
	pLib->Run();
	 
	delete pLib;
	return 0;
}

在这里插入图片描述

模式定义

定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。
——《设计模式》GoF

结构(Structure)

在这里插入图片描述

要点总结

 Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制**(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构**。

 除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用

 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法

【2】Strategy 策略模式

动机(Motivation)💡:

  • 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。

**问题思考(Consider)**🤔:

  • 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?

C++执行代码:

strategy1.cpp(无使用Strategy策略模式)

若想新增法国税法,如何实现?

#include<iostream>
using namespace std;
enum TaxBase {
    CN_Tax,
    US_Tax,
    DE_Tax,
    FR_Tax       //更改(增加法国税法计算方式)
};

class SalesOrder {
    TaxBase tax;
    double sum = 0;
public:
    double CalculateTax(TaxBase tax) {
        //...
        if (tax == CN_Tax) {
            //CN***********
            cout << "中国税法计算" << endl;
            sum += 1;
            
        }
        else if (tax == US_Tax) {
            //US***********
            cout << "美国税法计算" << endl;
            sum += 2;
        }
        else if (tax == DE_Tax) {
            //DE***********
            cout << "德国税法计算" << endl;
            sum += 3;
        }
        else if (tax == FR_Tax) {  //更改(增加法国税法计算方式)
            cout << "法国税法计算" << endl;
            sum += 4;
        }

        //....
        return sum;
    }

};

int main() {
    TaxBase tax;
    tax = FR_Tax;
    SalesOrder *so =  new SalesOrder();
    cout << so->CalculateTax(tax) << endl;
    return 0;
}

缺点:第一种方法需要去改枚举类型,需要往方法内部补充一些代码,打破了开放封闭原则,违背了复用性(二进制层面的复用性)。

strategy2.cpp(使用Strategy策略模式)

// 这两个类的具体实现不重要
class Context {

};

class StrategyFactory {
public:
    TaxStrategy* NewStrategy() {
        return nullptr; // ...
    }
};

//税率策略模式类
class TaxStrategy {
public:
    virtual double Calculate(const Context& context) = 0;
    virtual ~TaxStrategy() {}
};

//CNTax 中国税法
class CNTax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //***********
    }
};

//USTax 美国税法
class USTax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //***********
    }
};

//DETax 德国税法
class DETax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //***********
    }
};


//扩展
//*********************************
//FRTax 法国税法
class FRTax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //.........
    }
};

//不需要变化的
class SalesOrder {
private:
    TaxStrategy* strategy;

public:
    // 工厂模式
    SalesOrder(StrategyFactory* strategyFactory) {
        this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder() {
        delete this->strategy;
    }

    double CalculateTax() {
        //...
        Context context;

        double val = strategy->Calculate(context); //多态调用
        //...
    }

};

第二种写法遵循了开放封闭原则

模式定义

定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。

——《设计模式》GoF

结构(Structure)

在这里插入图片描述

要点总结

 Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。

 Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。

 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。

【3】Observer 观察者模式

动机(Motivation)💡:

 在软件构建过程中,我们需要为某些对象建立一种**“通知依赖关系”** ——一个对象(目标对象)的状态发生改变,所有的依赖对

象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。

 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合

**问题思考(Consider)**🤔:

C++执行代码:

FileSplitter1.cpp

#include <string>
#include <iostream>
using std::string;

class ProgressBar{
public:
	void setValue(float value){
		// ...
	}
};

class FileSplitter
{
	string m_filePath;
	int m_fileNumber;
	ProgressBar* m_progressBar;//具体通知机制

public:
	FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) :
		m_filePath(filePath), 
		m_fileNumber(fileNumber),
		m_progressBar(progressBar){

	}

	void split(){

		//1.读取大文件

		//2.分批次向小文件中写入
		for (int i = 0; i < m_fileNumber; i++){
			//...
			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;
			m_progressBar->setValue(progressValue);
		}

	}
};

MainForm1.cpp

#include <string>
#include <iostream>
using std::string;

// 以下几个类的具体实现不重要
class Form{

};

class TextBox{
public:
	string getText(){
		// ...
		return "";
	}
};

class ProgressBar;

class FileSplitter{
public:
	FileSplitter(string filePath, int number, ProgressBar* progressBar){
		// ...
	}

	void split(){
		// ...
	}
};



// 
class MainForm : public Form
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;

public:
	void Button1_Click(){

		string filePath = txtFilePath->getText();
		int number = atoi(txtFileNumber->getText().c_str());

		FileSplitter splitter(filePath, number, progressBar);

		splitter.split();

	}
};


FileSplitter1.cpp

class IProgress{
public:
	virtual void DoProgress(float value)=0;
	virtual ~IProgress(){}
};


class FileSplitter
{
	string m_filePath;
	int m_fileNumber;
	//ProgressBar* m_progressBar; //具体通知机制
	//IProgress* m_iprogressList;//抽象通知机制,支持1个观察者
	List<IProgress*>  m_iprogressList; // 抽象通知机制,支持多个观察者
	
public:
	FileSplitter(const string& filePath, int fileNumber) :
		m_filePath(filePath), 
		m_fileNumber(fileNumber){

	}


	void split(){

		//1.读取大文件

		//2.分批次向小文件中写入
		for (int i = 0; i < m_fileNumber; i++){
			//...

			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;
			onProgress(progressValue);//发送通知
		}

	}


	void addIProgress(IProgress* iprogress){
		m_iprogressList.push_back(iprogress);
	}

	void removeIProgress(IProgress* iprogress){
		m_iprogressList.remove(iprogress);
	}


protected:
	virtual void onProgress(float value){
		
		List<IProgress*>::iterator itor=m_iprogressList.begin();

		while (itor != m_iprogressList.end() )
			(*itor)->DoProgress(value); //更新进度条
			itor++;
		}
	}
};

MainForm2.cpp

class MainForm : public Form, public IProgress
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;

	ProgressBar* progressBar;

public:
	void Button1_Click(){

		string filePath = txtFilePath->getText();
		int number = atoi(txtFileNumber->getText().c_str());

		ConsoleNotifier cn;

		FileSplitter splitter(filePath, number);

		splitter.addIProgress(this); //订阅通知
		splitter.addIProgress(&cn)//订阅通知

		splitter.split();

		splitter.removeIProgress(this);

	}

	virtual void DoProgress(float value){
		progressBar->setValue(value);
	}
};

class ConsoleNotifier : public IProgress {
public:
	virtual void DoProgress(float value){
		cout << ".";
	}
};


网上代码(来子这个博主):https://github.com/chouxianyu/design-patterns-cpp

Observer.cpp:

#include <iostream>
#include <vector>

class Subject;

/// Observer 和 ConcreteObserver ///
//抽象观察者
class Observer
{
public:
	virtual ~Observer() {}

	virtual int getState() = 0;
	virtual void update(Subject* subject) = 0;
};

//具体观察者
class ConcreteObserver : public Observer
{
public:
	ConcreteObserver(const int state) :
		observer_state(state) {} //osbserver_state = state

	//析构函数
	~ConcreteObserver() {}

	//获取observer_state
	int getState()
	{
		return observer_state;
	}
	
	void update(Subject* subject);

private:
	int observer_state;
};

/// Subject 和 ConcreteObserver ///

class Subject
{
public:
	virtual ~Subject() {}
	//往observers添加observer
	void attach(Observer* observer)
	{
		observers.push_back(observer);
	}
	//从observers删除指定index的observer
	void detach(const int index)
	{
		observers.erase(observers.begin() + index);
	}
	//遍历observers,调用ConcreteObserver的update函数
	void notify()
	{
		for (unsigned int i = 0; i < observers.size(); i++)
		{
			observers.at(i)->update(this);
		}
	}

	virtual int getState() = 0;
	virtual void setState(const int s) = 0;

private:
	std::vector<Observer*> observers;// 抽象通知机制,支持多个观察者
};

class ConcreteSubject : public Subject
{
public:
	~ConcreteSubject() {}

	int getState()
	{
		return subject_state;
	}

	void setState(const int s)
	{
		subject_state = s;
	}

private:
	int subject_state;
};


void ConcreteObserver::update(Subject* subject)
{
	observer_state = subject->getState();
	std::cout << "Observer state updated." << std::endl;
}

int main()
{
	ConcreteObserver observer1( 1 );
	ConcreteObserver observer2( 2 );
  
	std::cout << "Observer 1 state: " << observer1.getState() << std::endl;//Observer 1 state: 1
	std::cout << "Observer 2 state: " << observer2.getState() << std::endl;//Observer 2 state: 2
  
	Subject *subject = new ConcreteSubject();
	subject->attach( &observer1 ); //将observer1 放进observers
	subject->attach( &observer2 ); //将observer2 放进observers
  
	subject->setState( 10 );//设置subject的subject_state = 10
	subject->notify();//遍历observers,调用ConcreteObserver的update(Subject *subject)函数,将subject的subject_state=10 给 observers里的每一个对象设置 observer_state = 10
  
	std::cout << "Observer 1 state: " << observer1.getState() << std::endl;//Observer 1 state: 10
	std::cout << "Observer 2 state: " << observer2.getState() << std::endl;//Observer 2 state: 10
  
	delete subject;
	return 0;
}

在这里插入图片描述

模式定义

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

——《设计模式》GoF

结构(Structure)

在这里插入图片描述

要点总结

 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。

 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。

 观察者自己决定是否需要订阅通知,目标对象对此一无所知。

 Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。

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

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

相关文章

LLMs:OpenAI 官方文档发布提高 GPT 使用效果指南—GPT最佳实践(GPT best practices)翻译与解读

LLMs&#xff1a;OpenAI 官方文档发布提高 GPT 使用效果指南—GPT最佳实践(GPT best practices)翻译与解读 导读&#xff1a;为了获得优质输出&#xff0c;需要遵循几点基本原则&#xff1a; >> 写清楚指令&#xff1a;将任务和期望输出描述得尽可能清楚。GPT 无法读取您…

NLP——Question Answering 问答模型

文章目录 2 key approachesInformation retrieval-based QAQuestion Processing 问题处理Answer Types Retrieval 文档检索Answer Extraction 答案提取 Knowledge-based QASemantic Parsing 语义解析 Hybrid QAEvaluation 2 key approaches Information retrieval-based QA 基于…

JSBridge

在Hybrid模式下&#xff0c;H5会经常需要使用Native的功能&#xff0c;比如打开二维码扫描、调用原生页面、获取用户信息等&#xff0c;同时Native也需要向Web端发送推送、更新状态等&#xff0c;而JavaScript是运行在单独的JS Context中&#xff08;Webview容器、JSCore等&…

mysql select是如何一步步执行的呢?

mysql select执行流程如图所示 server侧 在8.0之前server存在查询语句对应数据的缓存&#xff0c;不过在实际使用中比较鸡肋&#xff0c;对于更新比较频繁、稍微改点查询语句都会导致缓存无法用到 解析 解析sql语句为mysql能够直接执行的形式。通过词法分析识别表名、字段名等…

IIC总线实验

IIC总线实验 一、IIC总线基础概念 1、I2C总线是PHLIPS公司在八十年代初推出的一种同步串行半双工总线&#xff0c;主要用于连接整体电路。 2、I2C总线为两线制&#xff0c;只有两根双向信号线 3、一根是数据线SDA&#xff0c;另一根是时钟线SCL 4、I2C硬件结构简单&#xf…

第四章 模型篇:模型训练与示例

文章目录 SummaryAutogradFunctions ()GradientBackward() OptimizationOptimization loopOptimizerLearning Rate SchedulesTime-dependent schedulesPerformance-dependent schedulesTraining with MomentumAdaptive learning rates optim.lr_scheluder Summary 在pytorch_t…

一分钟学一个 Linux 命令 - find 和 grep

前言 大家好&#xff0c;我是 god23bin。欢迎来到《一分钟学一个 Linux 命令》系列&#xff0c;每天只需一分钟&#xff0c;记住一个 Linux 命令不成问题。今天需要你花两分钟时间来学习下&#xff0c;因为今天要介绍的是两个常用的搜索命令&#xff1a;find 和 grep 命令。 …

Spring是什么?

目录 1、Spring的简介 2、Spring七大功能模块 3、Spring的优点 4、Spring的缺点 5、Sprig容器 6、Spring的生态圈&#xff08;重点&#xff09;***** 7、Spring中bean的生命周期 1、Spring的简介 Spring的英文翻译为春天&#xff0c;可以说是给Java程序员带来了春天&…

认识泛型

目录 什么是泛型 引出泛型 语法 泛型类的使用 语法 示例 类型推导(Type Inference) 裸类型(Raw Type) 小结&#xff1a; 泛型如何编译的 擦除机制 为什么不能实例化泛型类型数组 泛型方法 定义语法 泛型接口 泛型数组 什么是泛型 一般的类和方法&#xff0c…

013:解决vue中不能加载.geojson的问题

第013个 查看专栏目录: VUE — element UI 本文章目录 问题状态造成这个结果的原因&#xff1a;解决办法Vue Loader 其他特性&#xff1a;专栏目标 问题状态 在做vue项目的时候&#xff0c;碰到这样一个问题&#xff0c;vue页面中引用一个.geojson文件&#xff0c;提示如下错误…

Redis-原生命令

string 单值 set key value get key 对象 set user:1 value Mset user:1:name zhangsan user:1:sex man Mget user:1:name user:1:sex 分布式锁 setnx product:1001 true 计数器/全局序列号维护 incr article:readcount:{文章id} get article:readcount:{文章id} 哈希hash…

JavaEE语法第一章、计算机工作原理

【计算机科学速成课】[40集全/精校] - Crash Course Computer Science_哔哩哔哩_bilibili 目录 一、计算机发展史 二、冯诺依曼体系&#xff08;Von Neumann Architecture&#xff09; 三、CPU简单介绍 3.1CPU介绍 3.2并行和并发 四、操作系统&#xff08;Operating Syste…

【netty基础四】netty与nio

文章目录 一. 反应堆1. 堵塞模型2. Java NIO的工作原理 二. Netty与NIO 一. 反应堆 1. 堵塞模型 阻塞I/O在调用InputStream.read()方法时是阻塞的&#xff0c;它会一直等到数据到来&#xff08;或超时&#xff09;时才会返回&#xff1b; 同样&#xff0c;在调用ServerSocke…

PN7160 card emulation

AN13861.pdf 1 简介 本文档的目的是举例说明如何为特定的 CE 场景正确设置卡仿真 (CE)。 有关 CE 体系结构的详细说明&#xff0c;请查看用户手册 [5]。 卡仿真的硬件设置&#xff0c;参考[13]和[14]。 要求&#xff1a; • MCUXpresso 和/或Android 和/或Linux 的知识 • PN…

[游戏开发]Unity多边形分割为三角形_耳切法

[ 目录 ] 0. 前言1. 耳切法&#xff08;1&#xff09;基础的概念&#xff08;2&#xff09;耳点判断&#xff08;3&#xff09;判断角度类型&#xff08;4&#xff09;点是否在三角形内&#xff08;5&#xff09;判断顺逆时针 2. 耳切法小优化3. 耳切法实现&#xff08;1&#…

openGauss5 企业版之常用运维命令

文章目录 日维护检查项检查openGauss状态检查锁信息统计事件数据对象检查SQL报告检查备份基本信息检查 检查操作系统参数检查办法异常处理 检查openGauss健康状态检查办法 本章节主要介绍在 openGauss数据库 在日常运维中的常用命令 日维护检查项 检查openGauss状态 通过open…

Java性能权威指南-总结13

Java性能权威指南-总结13 堆内存最佳实践减少内存使用减少对象大小延迟初始化 堆内存最佳实践 减少内存使用 减少对象大小 对象会占用一定数量的堆内存&#xff0c;所以要减少内存使用&#xff0c;最简单的方式就是让对象小一些。考虑运行程序的机器的内存限制&#xff0c;增…

Nautilus Chain测试网迎阶段性里程碑,模块化区块链拉开新序幕

Nautilus Chain 是目前行业内少有的真实实践的 Layer3 模块化链&#xff0c;该链曾在几个月前上线了测试网&#xff0c;并接受用户测试交互。该链目前正处于测试网阶段&#xff0c;并即将在不久上线主网&#xff0c;这也将是行业内首个正式上线的模块化区块链底层。 而在上个月…

Android 13(T) Media框架 -异步消息机制

网上有许多优秀的博文讲解了Android的异步消息机制&#xff08;ALooper/AHandler/AMessage那一套&#xff09;&#xff0c;希望看详细代码流程的小伙伴可以去网上搜索。这篇笔记将会记录我对android异步消息机制的理解&#xff0c;这次学完之后就可以熟练运用这套异步消息机制了…

【数据库二】数据库用户管理与授权

数据库用户管理与授权 1.MySQL数据库管理1.1 常用的数据类型1.2 char和varchar区别1.3 SQL语句分类 2.数据表高级操作2.1 克隆表2.2 清空表2.3 创建临时表 3.MySQL的六大约束4.外键约束4.1 外键概述4.2 创建主从表4.3 主从表中插入数据4.4 主从表中删除数据4.5 删除外键约束 5.…