设计模式-原型适配器桥接外观

news2024/11/13 9:31:44

3.3原型模式(代码见vs)

        原型模式就是自我复制。原型模式的核心是一个clone()方法, 对于C++来说,其实就是拷贝构造函数,需要实现深拷贝。拷贝构造函数根据谁来拷贝才能实现自我复制呢?答案就是根据自己复制自己,即根据*this指针来复制。举例:打印后再复印,比直接打印500份省时省力气。

总结:

原型模式是一种不太常见的创建型模式,我们平时的工作中几乎不会遇到这种模式,但是在一些框架设计中会用到,这里我们只做了解即可。

应用场景:

1)当我们的对象类型不是开始就能确定的,而是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的对象比较容易一些;

2)有的时候,我们需要一个对象在某个状态下的副本,此时,我们使用原型模式是最好的选择;例如:一个对象,经过一段处理之后,其内部的状态发生了变化;这个时候,我们需要一个这个状态的副本,如果直接new一个新的对象的话,但是它的状态是不对的,此时,可以使用原型模式,将对象拷贝一个出来,这个对象就和此时状态下的对象是完全一致的了;

3)当我们处理一些比较简单的对象时,并且对象之间的区别很小,可能就几个属性不同而已,那么就可以使用原型模式来完成,省去了创建对象时的麻烦了;

4)有的时候,创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程。

原型模式练习

#include <iostream>
using namespace std;

//原型模式
//品牌抽象类
class Brand
{
public:
	virtual Brand* clone() = 0;//这个克隆方法是原型模式最核心的方法,其实就是实现拷贝构造
	virtual void show() = 0;
};
//具体品牌:美的
class Midea :public Brand
{
public:
	virtual Brand* clone() { return new Midea(*this); }
	virtual void show() { cout << "美的品牌" << endl; }
};
void test01()
{
	Brand* md = new Midea();//这是原型对象
	//接下来使用clone方法克隆更多的对象
	Brand* md1 = md->clone();
	Brand* md2 = md->clone();
	md->show();
	md1->show();
	md2->show();
	cout << md << "," << md1 << "," << md2 << endl;
	delete md;
	delete md1;
	delete md2;
}

3.4适配器模式(代码见vs)

在不改变源代码的情况下,将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以兼容。如下图所示:汽车和轨道之间增加了一个适配的板车。

可以将适配器理解为我们日常用的电源适配器: 家庭电压为220V,而手机充电是5V左右,所以需要适配。如下图:目标抽象类是现有的类,是我们要调用的接口,但我们实际想执行的功能接口是在另一个类中的,称之为被适配类,在不改变现有类代码的情况下,如何能实现目标?答案就是再写一个适配器类。

适配器可以分为两种实现方式: 类适配器和对象适配器。

类适配需要适配器类继承需要适配的两个类:目标类和被适配类,然后重写目标类的方法,使之变成被适配类的方法,这样就完成了适配,也就是说我们通过适配器类调用了目标类被重写的方法,方法接口不变,但实际执行的是被适配类的方法。如果不适配,目标类和被适配类因为方法接口不兼容,是无法通过调用目标类的方法去执行被适配类的方法的。

对象适配器则是把被适配类作为适配器的属性,适配器类只需要继承目标类,重写目标类的方法,通过这个属性调用被适配类的方法。

1.类适配器

//类适配器,现有接口类,不能改变现有接口
class RobotTarget
{
public:
	virtual void cry() = 0;
	virtual void move() = 0;
};
//被适配类,想调用的接口,通过这个接口来拓展功能
class DogAdaptee
{
public:
	void wang() { cout << "wang wang" << endl; }
	void run() { cout << "run run" << endl; }
};
//适配器类
class DogAdapter :public RobotTarget, public DogAdaptee
{
public:
	virtual void cry() { wang(); }//完成对原有接口cry的改造,调用的是wang方法
	virtual void move() { run(); }
};
void test02()
{
	//通过适配器对象来调用原有接口,实现新的功能
	RobotTarget* adapter = new DogAdapter();
	adapter->cry();
	adapter->move();
	delete adapter;
}

2.对象适配器

//对象适配器
class DogAdapter_obj :public RobotTarget
{
	DogAdaptee* mdog;//被适配类作为类属性
public:
	DogAdapter_obj(DogAdaptee* dog) :mdog(dog) {};
	virtual void cry() { mdog->wang(); }//完成对原有接口cry的改造,调用的是wang方法
	virtual void move() { mdog->run(); }
};
void test03()
{
	DogAdaptee* dog = new DogAdaptee();
	RobotTarget* adapter_obj = new DogAdapter_obj(dog);
	adapter_obj->cry();
	adapter_obj->move();
	delete dog;
	delete adapter_obj;
}

3.总结

在类适配器模式中,适配器与被适配者之间是继承关系。在对象适配器模式中,适配器与被适配者之间是关联关系;不论是类适配器还是对象适配器,适配器模式都将现有接口转化为客户类所期望的接口,实现了对现有类的复用(不用修改目标抽象类)。

推荐使用对象适配方式,因为耦合度低,符合合成复用原则。

4.适配器模式的应用场景:

在使用第三方组件时,可能需要将其接口适配为符合本系统需求的接口。

当一个系统的接口与外部系统的接口不兼容时,可以使用适配器模式进行适配。

在代码重构时,可能需要将已有代码的接口进行适配,以兼容新的接口要求。

在使用不同的类库或者框架时,可以使用适配器模式进行适配。

5.适配器模式练习

Printer和Banner类如下,分别用类适配器和对象适配器,完成Printer接口利用Banner的方法实现打印。Printer是目标类,Banner是被适配类。

//被适配类
class Banner
{
	string item;
public:
	Banner(string i) :item(i){}
	void showWeak() { cout << "......" << item << "......" << endl; }
	void showStrong() { cout << "******" << item << "******" << endl; }
};
//目标类
class Printer
{
public:
	virtual void printWeak() {};
	virtual void printStrong() {};
};
//类适配器
class ClassPrinter :public Banner, public Printer
{
public:
	ClassPrinter(string item):Banner(item){}
	virtual void printWeak() { showWeak(); }
	virtual void printStrong() { showStrong(); }
};
void test04()
{
	Printer* class_adapter = new ClassPrinter("我是类适配器");
	class_adapter->printWeak();
	class_adapter->printStrong();
	delete class_adapter;
}
//对象适配器
class ObjPrinter :public Printer
{
	Banner* banner;
public:
	ObjPrinter(Banner* ba):banner(ba){}
	virtual void printWeak() { banner->showWeak(); }
	virtual void printStrong() { banner->showStrong(); }
};
void test05()
{
	Banner* banner = new Banner("类适配器的Banner对象");
	Printer* obj_adapter = new ObjPrinter(banner);
	obj_adapter->printWeak();
	obj_adapter->printStrong();
	delete banner;
	delete obj_adapter;
}

3.5桥接模式(代码见vs)

通过桥接模式,将一个对象的多个变化因素关联起来。

1.实例

比如这样一个项目:我们的目的是实例化一个带颜色的图形。其中包括两个因素:颜色和形状。

        形状包括:圆形、长方形、三角形;

        颜色包括:蓝色,黄色,橙色。

        如下图:有两种方案,上面是继承方案,下面是桥接方案。继承方案就是让不同的具体图形继承抽象图形类,然后在每个具体图形类中实现具体的颜色,显然,这种方式造成了代码臃肿,每种颜色都要在每个图形中实现一遍。如果增加了新颜色,我们还要修改每个具体图形类的源代码。不满足开闭原则。

接下来看下面的桥接模式,将形状和颜色分离出来,都定义成抽象类和具体实现类,然后通过某个抽象类桥接另一个抽象类,让他们产生关系,这样就可以进行灵活的组合,

具体做法如下:

图形类抽象类作为桥接类,起到关联图形对象不同的变化因素的作用。桥接类作为抽象父类,把需要关联的变化因素作为属性,比如将颜色抽象类作为属性关联过来。具体的形状类继承图形抽象类,然后再跟具体的颜色关联起来,这样具体形状就跟具体颜色建立了联系。具体形状类的内部就可以使用不同的颜色(就是使用父类的属性)。可以实现任何颜色和任何形状的组合。

这样做的好处:后期如果我们需要扩展颜色或者形状,可以直接新增颜色的子类和形状的子类,不用修改源代码,只需要通过增加代码就可以实现。支持开闭原则

//桥接模式
//颜色抽象类
class Color
{
public:
	virtual void Paint() = 0;
};
//具体颜色:红色
class Red :public Color
{
public:
	virtual void Paint() { cout << "红色的" << endl; }
};
//具体颜色:蓝色
class Blue :public Color
{
public:
	virtual void Paint() { cout << "蓝色的" << endl; }
};
//形状抽象类
class Shape
{
protected:
	Color* mcolor;//将颜色类桥接过来
public:
	Shape(Color* color):mcolor(color){}
	virtual void draw() = 0;
};
//具体形状:矩形
class Rectangle :public Shape
{
public:
	Rectangle(Color* color) :Shape(color) {};//先构造父类,需要使用一个Color对象
	virtual void draw() { cout << "形状是矩形,颜色是:"; mcolor->Paint(); }
};
//具体形状:圆形
class Circle :public Shape
{
public:
	Circle(Color* color) :Shape(color) {};//先构造父类,需要使用一个Color对象
	virtual void draw() { cout << "形状是圆形,颜色是:"; mcolor->Paint(); }
};
void test06()
{
	//构造一个蓝色的矩形
	Color* c = new Blue();
	Shape* sh = new Rectangle(c);
	sh->draw();
	delete c;
	delete sh;

	//构造一个红色的圆形
	Color* c1 = new Red();
	Shape* sh1 = new Circle(c1);
	sh1->draw();
	delete c1;
	delete sh1;
}

2. 桥接总结

常用的场景:

当我们考虑一个对象的多个因素可以动态变化的时候,考虑使用桥接模式,如上面例子中的支付方式和认证方式可能随着技术的发展不断变化,所以将他们分离出来,然后使用桥接模式联系起来,同时它们可以独立变化。可以在不修改源代码的情况下,增加新的支付方式。

优点:

抽象和实现的分离。

优秀的扩展能力,支持开闭原则。

实现细节对客户透明。(抽象与实现是如何组合的是透明的)

缺点:

客户必须知道选择哪一种类型的实现。

3.桥接模式练习

有个需求,点奶茶,奶茶容量:分为大,小杯。奶茶口味:可以添加牛奶,水果。用桥接模式实现,可以点不同容量不同口味组合的奶茶,同时方便后期更换杯子容量和口味。

//口味基类
class Taste
{
public:
	virtual void addSomething() = 0;
};
//具体口味:牛奶
class MilkTaste :public Taste
{
	virtual void addSomething() { cout << "加奶" << endl; }
};
//具体口味:水果
class FruitTaste :public Taste
{
	virtual void addSomething() { cout << "加水果" << endl; }
};
//奶茶基类,桥接类
class Tea
{
protected:
	Taste* taste;
public:
	Tea(Taste* t) :taste(t) {};
	virtual void orderTea(int count) = 0;
};
//小杯容量奶茶
class SmallTea :public Tea
{
public:
	SmallTea(Taste* t) :Tea(t) {};
	virtual void orderTea(int count)
	{
		cout << "下单" << count << "小杯" << endl;
		taste->addSomething();
	}
};
//大杯容量奶茶
class LargeTea :public Tea
{
public:
	LargeTea(Taste* t) :Tea(t) {};
	virtual void orderTea(int count)
	{
		cout << "下单" << count << "大杯" << endl;
		taste->addSomething();
	}
};
void test07()
{
	//需要两大杯牛奶味奶茶
	Taste* milk = new MilkTaste();
	Tea* t = new LargeTea(milk);
	t->orderTea(2);
	delete milk;
	delete t;
}

3.6外观模式(代码见vs)

外观模式又称为门面模式,它是一种对象结构型模式。外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观对象关联了子系统各个对象,作为自己的属性来使用它们的方法。

案例:

子系统:智能馒头机,功能有:和面,发酵,揉面,蒸;

界面:外观模式类,提供统一的接口。

//外观模式
//和面子系统
class MixDough
{
public:
	void doMix() { cout << "水和面按照固定比例混合均匀,并加入发酵粉,搅拌15分钟。" << endl; }
};
//发酵子系统
class Ferment
{
public:
	void doFerment() { cout << "在合适的温度下密封静置6小时" << endl; }
};
//揉面子系统
class Knead
{
public:
	void doKnead() { cout << "加入面粉或者玉米粉,揉面5分钟,然后做出馒头的形状" << endl; }
};
//蒸锅子系统
class Steam
{
public:
	void boilWater() { cout << "加水点火" << endl; }
	void doSteam() { cout << "放入馒头,烧开后,蒸20分钟" << endl; }
};
//外观界面,让用户一键启动
class Facade
{
	MixDough* mixDough;
	Ferment* ferment;
	Knead* knead;
	Steam* steam;
public:
	Facade()
	{
		mixDough = new MixDough();
		ferment = new Ferment();
		knead = new Knead();
		steam = new Steam();
	}
	~Facade()
	{
		if (mixDough!=nullptr)
		{
			delete mixDough;
			mixDough = nullptr;
		}
		if (ferment!=nullptr)
		{
			delete ferment;
			ferment = nullptr;
		}
		if (knead!=nullptr)
		{
			delete knead;
			knead = nullptr;
		}
		if (steam!=nullptr)
		{
			delete steam;
			steam = nullptr;
		}
	}
	void work()
	{
		//一键启动,完成各个子系统的调度
		mixDough->doMix();
		ferment->doFerment();
		knead->doKnead();
		steam->boilWater();
		steam->doSteam();
		cout << "馒头已蒸好!" << endl;
	}
};
void test08()
{
	Facade* f = new Facade();
	f->work();
	delete f;
}

总结:

优点:

根据“单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象(相当于控制层,满足最少知道原则),它为子系统的访问提供了一个简单而单一的入口。

外观模式也是“迪米特法则”的体现,通过引入一个新的外观类可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度。

外观模式从很大程度上提高了客户端使用的便捷性,降低了系统的复杂度。使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。

使用场景:

当你要为一个复杂子系统提供一个简单接口时。

外观模式练习

使用外观模式,模拟代码编译的过程。

//语法检查子系统
class CSyntaxParser
{
public:
	void syntaxParser() { cout << "开始语法检查..." << endl; }
};
//生成中间代码子系统
class CGenMidCode
{
public:
	void genMidCode() { cout << "生成中间代码..." << endl; }
};
//生成汇编语言子系统
class CGenAssemblyCode
{
public:
	void genAssemblyCode() { cout << "生成汇编语言..." << endl; }
};
//链接可执行程序子系统
class CLinkSystem
{
public:
	void linkSystem() { cout << "链接生成可执行程序..." << endl; }
};
//界面类
class ComileFacade
{
	CSyntaxParser* syntax;
	CGenMidCode* midCode;
	CGenAssemblyCode* assemblyCode;
	CLinkSystem* link;
public:
	ComileFacade()
	{
		syntax = new CSyntaxParser();
		midCode = new CGenMidCode();
		assemblyCode = new CGenAssemblyCode();
		link = new CLinkSystem();
	}
	~ComileFacade()
	{
		if (syntax != nullptr)
		{
			delete syntax;
			syntax = nullptr;
		}
		if (midCode != nullptr)
		{
			delete midCode;
			midCode = nullptr;
		}
		if (assemblyCode != nullptr)
		{
			delete assemblyCode;
			assemblyCode = nullptr;
		}
		if (link != nullptr)
		{
			delete link;
			link = nullptr;
		}
	}
	void compile()
	{
		syntax->syntaxParser();
		midCode->genMidCode();
		assemblyCode->genAssemblyCode();
		link->linkSystem();
		cout << "编译完成!" << endl;
	}
};
void test09()
{
	ComileFacade* f = new ComileFacade();
	f->compile();
	delete f;
}

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

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

相关文章

语言质量评估对欧洲游戏推广的重要性

语言质量评估在欧洲推广游戏中的重要性怎么强调都不为过。欧洲是一个文化和语言多样化的市场&#xff0c;整个大陆有200多种语言。因此&#xff0c;要提供一款与不同地区玩家产生共鸣的本地化游戏&#xff0c;不仅需要准确的翻译&#xff0c;还需要细致的语言质量评估。这一过程…

mysql高级sql

一&#xff0c;查询 1.按关键字排序 1.1用 **ORDER BY** 语句来实现排序&#xff1a; - ORDER BY 语句用于对查询结果进行排序。可以根据一个或多个字段的值进行升序&#xff08;ASC&#xff09;或降序&#xff08;DESC&#xff09;排序。1.2排序可针对一个或多个字段&#…

【运维监控】prometheus+node exporter+grafana 监控linux机器运行情况(2)

本示例是通过prometheus的node exporter收集主机的信息&#xff0c;然后在grafana的dashborad进行展示。本示例使用到的组件均是最新的&#xff0c;下文中会有具体版本说明&#xff0c;linux环境是centos。本示例分为四个部分&#xff0c;即prometheus、grafana、node exporter…

W25Q32驱动 基于HAL库的SPI

环境 芯片:STM32F103ZET6 库&#xff1a;来自HAL的STM32F1XX.H 原理图 cubeMX配置 W25Q32.h // // Created by seven on 2024/9/2. //#ifndef SPI_W25Q32_H #define SPI_W25Q32_H #include "spi.h"/*** 初始化W25Q32闪存芯片*/ void W25Q32_Init(void);/*** 停止…

P7492 [传智杯 #3 决赛] 序列

*原题链接* 一道类似势能线段树的题&#xff0c;区间按位或上k&#xff0c;不满足区间可合并的性质&#xff0c;只能暴力的单点修改。 但是考虑按位或的性质&#xff0c;一个数或上另一个数&#xff0c;只会变大或不变&#xff0c;如果我们能找到一个方法&#xff0c;能够判定…

Gitlab-ce upgrade 16.0.1 to 17.3.1【Gitlab-ce 16.0.1 升级 17.3.1】

文章目录 背景gitlab-ce 16.0.1 升级 17.3.1 失败gitlab-ce 16.0.1 升级 16.11.8 失败gitlab-ce 16.0.1 升级 16.7.9 失败gitlab-ce 16.0.1 升级 16.3.8 成功gitlab-ce 16.3.8 升级 16.11.8 失败gitlab-ce 16.3.8 升级 16.7.9 成功gitlab-ce 16.7.8 升级 16.11.8 成功gitlab-ce…

牛客(数字统计)

[NOIP2010]数字统计 题目题解(77)讨论(41)排行面经 new 时间限制&#xff1a;1秒 空间限制&#xff1a;128M 知识点NOIP复赛字符串 校招时部分企业笔试将禁止编程题跳出页面&#xff0c;为提前适应&#xff0c;练习时请使用在线自测&#xff0c;而非本地IDE。 描述 请统计…

【Day08】

目录 MySQL-多表查询-概述 MySQL-多表查询-内连接 MySQL-多表查询-外连接 MySQL-多表查询-[标量、列]子查询 MySQL-多表查询-[行、表]子查询 MySQL-多表查询-案例 MySQL-事务-介绍与操作 MySQL-事务-四大特性 MySQL-索引-介绍 MySQL-索引-结构 MySQL-索引-操作语法 …

【Qt】消息对话框 QMessageBox

消息对话框 QMessageBox QMessageBox 用于显示一个消息给用户&#xff0c;&#xff0c;并且让用户进行一个简单的选择。 消息对话框是应⽤程序中最常⽤的界⾯元素。消息对话框主要⽤于为⽤⼾提⽰重要信息&#xff0c;强制⽤⼾进⾏选择操作。 例子&#xff1a;创建一个消息对话…

【Datawhale X 李宏毅苹果书 AI夏令营】《深度学习详解》Task3 打卡

文章目录 前言学习目标一、优化策略二、模型偏差三、优化问题三、过拟合增加训练集给模型一些限制 四、交叉验证五、不匹配总结 前言 本文是【Datawhale X 李宏毅苹果书 AI夏令营】的Task3学习笔记打卡。 学习目标 李宏毅老师对应视频课程&#xff1a;https://www.bilibili.…

JMeter 安装使用

JMeter 安装使用 a.安装 下载链接:Apache JMeter - Download Apache JMeter 环境变量 打开 cmd 输入 jmeter&#xff0c;即可启动 b.使用 http请求接口 300 个线程设置 1 s 的预热时间 右键 start

B端:工作台页面放什么?不知道,这里告诉你10个常见内容。

工作台是B端系统的核心页面&#xff0c;也是最常用的页面&#xff0c;该页面的上通常放哪些内容了&#xff0c;是中说纷纭&#xff0c;本文把常放内容给大家列举下。 B端工作台页面是专门为企业用户设计的工作台&#xff0c;通常需要包含一些与企业工作相关的功能和信息。以下…

传知代码-让机器感受你的情绪!(论文复现)

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 让计算机理解你的情绪&#xff01; 随着近年来社交媒体的快速增长&#xff0c;社交媒体上的用户生成内容&#xff08;例如视频&#xff09;的量大幅增加。不同模态的自动情感分析有利于人机交互&#xff0c;并吸…

跟我一起学FPGA (二) 语法讲解

本章讲解 1 、Verilog 概述和基础知识&#xff1b; 2 、Verilog 程序框架和高级知识点&#xff1b; 3 、Verilog 编程规范。 1.Verilog 概述和基础知识 Verilog 是一种硬件描述语言&#xff0c;以文本形式来描述数字系统硬件的结构和行为的语言&#xff0c;用它可以表示逻辑电…

Vivado2018.3版本_将bit文件转化为bin文件

Vivado2018.3版本_将bit文件转化为bin文件 1、配置转换文件 2、下载到flash中

用 count(*)哪个存储引擎会更快?

InnoDB 引擎执行 count 函数的时候&#xff0c;需要通过遍历的方式来统计记录个数&#xff0c;而 MyISAM 引擎执行 count 函数只需要 0(1 )复杂度&#xff0c;这是因为每张 MyISAM 的数据表都有一个 meta 信息有存储了row_count值&#xff0c;由表级锁保证一致性&#xff0c;所…

[数据集][目标检测]鲜花检测数据集VOC+YOLO格式25215张106类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;25215 标注数量(xml文件个数)&#xff1a;25215 标注数量(txt文件个数)&#xff1a;25215 标…

给P人一点小小震撼!铁威马UI全新设计!更友好更美观~

开学了&#xff0c;小马居然发现有学校竟然玩起了新花样&#xff0c;通过MBTI来分宿舍&#xff0c;这脑洞大开的创意简直让人直呼内行&#xff01; 你是P人还是J人&#xff1f; 在MBTI人格分类中&#xff0c;J人喜欢条理清晰的计划&#xff0c;而P人则倾向于随性所欲。 想象…

第十五届北京青年相声节宣传片发布

第十五届北京青年相声节宣传片&#xff0c;围绕相声演员的四个基本功&#xff1a;说、学、逗、唱&#xff0c;搭建四个生活场景&#xff0c;编创四个微短故事&#xff0c;演绎四组人物关系&#xff0c;向大众普及“相声就在我们身边”的深刻内涵。

如何轻松的步入编程的第一课?相信你就是那个伟大的天才!

第一次编程&#xff0c;会让初学者感到既兴奋又有些紧张。以下是一些建议 明确学习目标&#xff1a;首先&#xff0c;思考你为什么想学编程。是为了职业发展、兴趣爱好、还是仅仅想了解新技术&#xff1f;明确的目标能帮你保持学习的动力。选择合适的编程语言&#xff1a;对于…