设计模式之原型模式--Prototype Pattern

原型模式的用意是:通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建出更多的同类型对象。

从孙大圣的手段谈起

孙悟空在与黄风怪的战斗中,"使一个身外身的手段:把毫毛揪下一把,用口嚼得粉碎,望上一喷,叫声'变',变有百十个行者,都是一样得打扮,各执一根铁棒,把那怪围在空中。"换而言之,孙悟空可以根据自己的形象,复制出很多"身外身"来。

老孙这种身外身的手段在面向对象设计领域里叫原型(Prototype)模式。

C#对原型模式的支持

在C#里面,我们可以很容易的通过Clone()方法实现原型模式。任何类,只要想支持克隆,必须实现C#中的ICloneable接口。ICloneable接口中有一Clone方法,可以在类中复写实现自定义的克隆方法。克隆的实现方法有两种:浅拷贝(shallow copy)与深拷贝(deep copy)。

(以下摘自:《.NET框架程序设计(修订版)》,李建忠译)浅拷贝是指当对象的字段值被拷贝时,字段引用的对象不会被拷贝。例如,如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅拷贝,那么两个对象将引用同一个字符串。而深拷贝是对对象实例中字段引用的对象也进行拷贝的一种方式,所以如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个深拷贝的话,我们将创建一个新的对象和一个新的字符串--新对象将引用新字符串。需要注意的是执行深拷贝后,原来的对象和新创建的对象不会共享任何东西;改变一个对象对另外一个对象没有任何影响。


二、 Prototype模式的结构:

客户(Client)角色:客户类提出创建对象的请求。
抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象原型角色所要求的接口。


三、 程序举例:

下面的程序给出了一个示意性的实现:

// Prototype pattern -- Structural example 
using System;

// "Prototype"
abstract class Prototype
{
 // Fields
 private string id;

 // Constructors
 public Prototype( string id )
 {
 this.id = id;
 }


 public string Id
 {
 get{ return id; }
 }


 // Methods
 abstract public Prototype Clone();
}


// "ConcretePrototype1"
class ConcretePrototype1 : Prototype
{
 // Constructors
 public ConcretePrototype1( string id ) : base ( id ) {}

 // Methods
 override public Prototype Clone()
 {
 // Shallow copy
 return (Prototype)this.MemberwiseClone();
 }

}


// "ConcretePrototype2"
class ConcretePrototype2 : Prototype
{
 // Constructors
 public ConcretePrototype2( string id ) : base ( id ) {}

 // Methods
 override public Prototype Clone()
 {
 // Shallow copy
 return (Prototype)this.MemberwiseClone();
 }

}


/// <summary>
/// Client test
/// </summary>

class Client
{
 public static void Main( string[] args )
 {
 // Create two instances and clone each
 ConcretePrototype1 p1 = new ConcretePrototype1( "I" );
 ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
 Console.WriteLine( "Cloned: {0}", c1.Id );

 ConcretePrototype2 p2 = new ConcretePrototype2( "II" );
 ConcretePrototype2 c2 = (ConcretePrototype2)p2.Clone();
 Console.WriteLine( "Cloned: {0}", c2.Id );
 }

}

这个例子实现了一个浅拷贝。其中MemberwiseClone()方法是Object类的一个受保护方法,实现了对象的浅拷贝。如果希望实现一个深拷贝,应该实现ICloneable接口,并自己编写ICloneable的Clone接口方法。


四、 带Prototype Manager的原型模式

原型模式的第二种形式是带原型管理器的原型模式,其UML图如下:

客户(Client)角色:客户端类向原型管理器提出创建对象的请求。
抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。


下面这个例子演示了在原型管理器中存储用户预先定义的颜色原型,客户通过原型管理器克隆颜色对象。

// Prototype pattern -- Real World example 
using System;
using System.Collections;

// "Prototype"
abstract class ColorPrototype
{
 // Methods
 public abstract ColorPrototype Clone();
}


// "ConcretePrototype"
class Color : ColorPrototype
{
 // Fields
 private int red, green, blue;

 // Constructors
 public Color( int red, int green, int blue)
 {
 this.red = red;
 this.green = green;
 this.blue = blue;
 }


 // Methods
 public override ColorPrototype Clone()
 {
 // Creates a 'shallow copy'
 return (ColorPrototype) this.MemberwiseClone();
 }


 public void Display()
 {
 Console.WriteLine( "RGB values are: {0},{1},{2}",
 red, green, blue );
 }

}


// Prototype manager
class ColorManager
{
 // Fields
 Hashtable colors = new Hashtable();

 // Indexers
 public ColorPrototype this[ string name ]
 {
 get{ return (ColorPrototype)colors[ name ]; }
 set{ colors.Add( name, value ); }
 }

}


/// <summary>
/// PrototypeApp test
/// </summary>

class PrototypeApp
{
 public static void Main( string[] args )
 {
 ColorManager colormanager = new ColorManager();

 // Initialize with standard colors
 colormanager[ "red" ] = new Color( 255, 0, 0 );
 colormanager[ "green" ] = new Color( 0, 255, 0 );
 colormanager[ "blue" ] = new Color( 0, 0, 255 );

 // User adds personalized colors
 colormanager[ "angry" ] = new Color( 255, 54, 0 );
 colormanager[ "peace" ] = new Color( 128, 211, 128 );
 colormanager[ "flame" ] = new Color( 211, 34, 20 );

 // User uses selected colors
 string colorName = "red";
 Color c1 = (Color)colormanager[ colorName ].Clone();
 c1.Display();

 colorName = "peace";
 Color c2 = (Color)colormanager[ colorName ].Clone();
 c2.Display();

 colorName = "flame";
 Color c3 = (Color)colormanager[ colorName ].Clone();
 c3.Display();
 }

}



五、 浅拷贝与深拷贝

下面给出浅拷贝与深拷贝的两个例子,例子使用了ICloneable接口。C#中的数组是引用型的变量,我们通过数组来进行演示:

浅拷贝:

using System;

class ShallowCopy : ICloneable
{
 public int[] v = {1,2,3};

 public Object Clone()
 {
 return this.MemberwiseClone();
 }


 public void Display()
 {
 foreach(int i in v)
 Console.Write( i + ", ");
 Console.WriteLine();
 }

}


class Client
{
 public static void Main()
 {
 ShallowCopy sc1 = new ShallowCopy();
 ShallowCopy sc2 = (ShallowCopy)sc1.Clone();
 sc1.v[0] = 9;

 sc1.Display();
 sc2.Display();
 }

}


ShallowCopy对象实现了一个浅拷贝,因此当对sc1进行克隆时,其字段v并没有克隆,这导致sc1与sc2的字段v都指向了同一个v,因此,当修改了sc1的v[0]后,sc2的v[0]也发生了变化。

深拷贝:

using System;

class DeepCopy : ICloneable
{
 public int[] v = {1,2,3};

 // 默认构造函数
 public DeepCopy()
 {
 }


 // 供Clone方法调用的私有构造函数
 private DeepCopy(int[] v)
 {
 this.v = (int[])v.Clone();
 }


 public Object Clone()
 {
 // 构造一个新的DeepCopy对象,构造参数为
 // 原有对象中使用的 v 
 return new DeepCopy(this.v);
 }


 public void Display()
 {
 foreach(int i in v)
 Console.Write( i + ", ");
 Console.WriteLine();
 }

}


class Client
{
 public static void Main()
 {
 DeepCopy dc1 = new DeepCopy();
 DeepCopy dc2 = (DeepCopy)dc1.Clone();
 dc1.v[0] = 9;

 dc1.Display();
 dc2.Display();
 }

}


这次在克隆的时候,不但克隆对象本身,连里面的数组字段一并克隆。因此,最终打印出来的dc1与dc2不同。


六、 Prototype模式的优点与缺点

Prototype模式的优点包括

1、Prototype模式允许动态增加或减少产品类。由于创建产品类实例的方法是产批类内部具有的,因此增加新产品对整个结构没有影响。

2、Prototype模式提供了简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而Prototype模式就不需要这样。

3、Portotype模式具有给一个应用软件动态加载新功能的能力。由于Prototype的独立性较高,可以很容易动态加载新功能而不影响老系统。

4、产品类不需要非得有任何事先确定的等级结构,因为Prototype模式适用于任何的等级结构。


Prototype模式的缺点:

Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。

转载于:https://www.cnblogs.com/tzh521241/archive/2012/11/11/PrototypePattern.html

LAN8720芯片硬件设计避坑指南:从晶振选择到PHY地址配置的5个关键细节 本文深入解析LAN8720以太网PHY芯片硬件设计的五个关键细节,涵盖晶振模式选择、PHY地址配置、复位电路设计、电源滤波与接口布线。重点阐述了REF_CLK Out与REF_CLK In两种时钟模式的配置逻辑与应用场景,提供了避免通信失败与信号完整性问题的实战方案,是嵌入式网络硬件开发的实用避坑指南。 阅读详情

相关推荐

python——体育收入排行2012-2019

请分析文件并读取数据,完成以下功能: 1. 某年的收入排名前若干名选手 如果输入n是2012-19之间的年份,则可以输入整数k,输出n年的前k名排名信息。如果k大于当年排名运动员人数,则输出当年所有的信息。每行一名运动员信息,以' | '间隔该行各项信息。 2. 某年属于某个运动项目的所有运动员信息,和收入总和 如果输入的是'sport'(不区分大小写),则可以继续输入一个年份201*,输出一个当年排行中的运动项目菜单,按照字符串升序排列,格式见示例,再输入运动项目对应的数字来输出该项运动当

m0_62488776的博客 2257

原型(Prototype)模式

一、 原型(Prototype)模式原型模式的用意是:通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建出更多的同类型对象。从孙大圣的手段谈起孙悟空在与黄风怪的战斗中,"使一个身外身的手段:把毫毛揪下一把,用口嚼得粉碎,望上一喷,叫声变,变有百十个行者,都是一样得打扮,各执一根铁棒,把那怪围在空中。"换而言之,孙悟空可以根据自己的形象,复制出很多"身外身"来。老孙这

旅行人生 1476

彻底解决0x80070005错误代码:全面分析与实战指南

0x80070005错误代码虽然令人头疼,但通过上述方法,我们完全有能力将其攻克。从检查磁盘空间、设置账号权限,到运行疑难解答和系统文件检查器,再到调整相关服务状态,每一步都是解决这一问题的关键。希望本文能为你提供有力的帮助,让你在使用Windows系统的过程中更加顺畅无忧。

nntxthml的博客 6935

java编程心得(十一)——Hashtable的深拷贝

今天编写项目时遇到一个逻辑错误: Hashtable ht2 = new Hashtable(); ht2 = ht1; 我以为像上面这样写就能实现在内存中将ht1复制一遍给ht2,但实际这样写是浅拷贝,ht2和ht1实际指向的是同一个地址,任意一个改变了hashtable的值另一个也相应的改变,就相当于ht2是ht1的引用。 要实现深拷贝,代码如下: Hashtable ht2 = new Hashtable(ht1); 这样写ht2就不单单是引用了,会有自己的内存空间。不过有一个问题,虽然都

Pc620的专栏 3520

树莓派用网线连着电脑,但是找不到它的ip 以及设置静态IP

第一次的话,树莓派默认是没有ssh的。需要把内存卡取出来,新建一个ssh文件,这个简单,新建一个txt文件重命名就好了。 如果前面配置了连接wifi的conf文件,它是会自动去连接wifi的,wifi连不上,它才会去尝试有线连接。所以把它的wifi关了,才会自动用网线连接。 另外,建议设置静态ip连接,这样避免网络连接突然中断。具体在etc/dhcpcd.conf文件中,把这几行前面的#去掉,然后后面的信息改成自己的就好了。 具体改法: 第一行是ip地址,这个找到自己电脑的ip段,然后改后面两个数字就好了

wander_ing的博客 1万+

检索生成(RAG) vs 长文本大模型:实际应用中如何选择?

大模型的上下文理解能力直接影响到 LLMs 在复杂任务和长对话中的表现。本期内容聚焦于两种主流技术:长上下文(Large Context Windows)和检索增强生成(RAG)。这两种技术各有何优势?在实际应用中,我们又该如何权衡选择?文章不仅详细阐述了这两种技术的工作原理,还深入分析了它们在效率、准确性和实施成本等方面的差异。文章特别提到了在执行重复性任务时使用 Context Caching 优化长上下文的优势,然而,缓存策略的效果很大程度上取决于提示词的可预测性。

2401_85782938的博客 1255

深入理解prototype(原型对象)

对JavaScript中原型模式的理解一:什么是原型对象?有什么优点?    简单的来说,无论何时,我们创建的每一个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象包含了通过调用该构造函数所创建的对象共享的属性和方法。其实我们平常的叫法就是指:prototype就是通过该构造函数创建的某个实例的原型对象,但是其实prototype是每个构造函数的属性而已,只能说万物皆

flyingpig2016的博客 3万+

南大计算机2024保研笔试:编译原理等5门专业课考点分布与408复习避坑指南

本文详细解析了南京大学计算机2024保研笔试的考点分布与复习策略,重点突出编译原理(占比28%-32%)和离散数学的特色考核。提供跨课程复习方法、真题题型分析及高效备考建议,帮助考生突破传统408框架,应对南大独特的命题风格。

weixin_29304985的博客 345

JavaScript 中的 .prototype:揭秘面向对象编程的基石

prototype属性是 JavaScript 中每个函数(包括构造函数)都拥有的一个特殊属性,它指向一个对象,称为原型对象。原型对象包含了该函数创建的所有实例共享的属性和方法。1.1prototype的本质 - 共享的宝库prototype属性并非函数本身的属性,而是函数的一个内部属性,它指向一个对象,称为原型对象。原型对象包含了该函数创建的所有实例共享的属性和方法。当我们访问一个实例的属性或方法时,JavaScript 会首先在实例本身查找,如果找不到,就会在原型对象中查找。1.2。

weixin_63490470的博客 1223

41页PPT | 华为业务流程架构全景视图:全业务域L1-L3级流程全案

TBD、收集和生成线索、验证机会点、TBD、管理销售授权、管理销售项目、管理销售项目群、管理合同要素/模板、验证和分发线索、标前引导、管理开票和回款、客户网络评估、管理销售评审、TBD、管理交付项目群、管理合同文档、管理结构化合同信息、跟踪和培育线索、制定并提交标书、管理合同/PO变更、客户网络规划、管理销售决策。:4.1 管理技术、服务请求、4.2管理备件、服务交付、4.3管理非技术、服务请求、4.4管理客户、投诉。:管理Idea、产生Idea、激发兴趣与协作、孵化、原型和验证、确认Idea与商业化。

zuoan1993的博客 4550

am62x A53与R5F通信

AM62x是Ti的一款异构多核芯片。remoteproc驱动,硬编码(写死)了R5F/M4F固件的名字,这样驱动启动时,就会找硬件编码的固件,下面是其对应关系。值得注意的是,linux内核中的M4F remoteproc驱动启动M4F,而linux内核中的R5F remoreteproc驱动不启动R5F,相反,它attach到已经在boot阶段启动的R5F固件上。在/lib/firmware的真实固件名:,而我们生成的固件名如下所示,没有直接对应的,经确认,.out文件就是我们需要的,重命名后即可。

鹰之翔 1361

prototype详解及prototype、new之间的关系

1.prototype解释 在JavaScript中,prototype对象是实现面向对象的一个重要机制。 每个函数就是一个对象(Function),函数对象都有一个子对象 prototype对象,类是以函数的形式来定义的。prototype表示该函数的原型,也表示一个类的成员的集合。 在通过new创建一个类的实例对象的时候,prototype对象的成员都成为实例化对象的成员

Turnhead的专栏 4216

【MATLAB】嵌入式PID控制参数整定与优化

本文提出了一种基于MATLAB仿真的嵌入式PID参数优化方法,解决了传统人工试凑法效率低、适应性差的问题。通过建立一阶滞后被控对象模型,对比分析了不同PID参数组合的性能表现,筛选出最优参数。仿真结果表明,优化后的PID参数能有效降低超调量(从传统方法的显著超调降至接近0%),缩短调节时间,提高稳态精度。最终将优化算法部署到STM32平台,实现了"仿真整定-参数优化-嵌入式落地"的完整开发流程。该方法显著提升了嵌入式系统的响应速度和控制精度,为工业控制应用提供了标准化解决方案。

unber的博客 45

设计模式-原型模式 Prototype Pattern

通常,如果需要快速复制对象并且对象的创建成本较高,或者需要创建多个相似但不完全相同的对象,原型模式是一个更好的选择。原型模式(Prototype Pattern)是一种创建型设计模式,它允许你通过复制现有的对象来创建新的实例,而不是通过新建的方式来构造。使用原型模式可以提高对象创建的效率,降低系统的资源消耗,并有助于实现对象的快速复制和灵活管理。然而,需要注意的是,原型模式可能会增加内存消耗,并且在处理包含引用类型成员的对象时需要特别小心,以避免浅拷贝导致的问题。

wesigj的博客 1699

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

设计模式-04原型模式Prototype Pattern

原型模式是一种创建型设计模式,通过复制已有对象来创建新对象,避免代码依赖具体类。它适用于需要动态创建对象、减少类层次结构或避免重复初始化复杂对象的场景。实现方式包括浅克隆(仅复制基本类型和引用)和深克隆(递归复制所有引用对象),后者可通过序列化或手动实现。示例代码展示了文档对象的克隆,浅克隆会共享引用导致原始对象被修改,而深克隆则完全独立,确保对象状态的完整性。

d3y1`5 81_09 885

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

23种设计模式之原型模式(Prototype Pattern)

本篇文章将23种设计模式中的单例设计模式,此篇文章为一天学习一个设计模式系列文章,后面会分享其他模式知识。

小威的博客 2万+

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

设计模式|原型模式(Prototype Pattern)

原型模式(Prototype Pattern)是一种创建型设计模式,其核心思想是通过复制现有对象来创建新对象,而无需显式地指定它们的类。这种模式通常用于当对象的创建成本较高,或者对象初始化过程比较复杂时。

weixin_44435110的博客 1420

设计模式之原型模式(Prototype Design Pattern)

原型模式,可以理解为克隆模式,就是对一个已创建的对象进行克隆。这个对象必须提供克隆的方法。 我们先看一个简单的例子: //实现Cloneable ,重写clone方法,使用对象提供克隆的能力 public class Article implements Cloneable { public Article(){ System.out.println("初始化"); } public Article clone() { System.out.

xjj1314的专栏 330

C++设计模式——原型模式(Prototype Pattern)

C++设计模式——原型模式(Prototype Pattern) 微信公众号:幼儿园的学霸 目录 文章目录C++设计模式——原型模式(Prototype Pattern)目录定义代码示例普通指针方式实现智能指针方式实现总结为什么需要原型模式优缺点 定义 Specify the kinds of Objects to create using a prototypical instance, and create new objects by copying this prototype. 用原型实例指

leonardohaig的博客 1636

设计模式之原型模式(Prototype Pattern)

意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。主要解决:在运行期建立和删除原型。何时使用:1、当一个系统应该独立于它的产品创建,构成和表示时。2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。3、为了避免创建一个与产品类层次平行的工厂类层次时。4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。

weixin_42294716的博客 859

原型模式(Prototype Design Pattern)及代码实现

什么是 原型模式? 原型模式 指 把一个通过类 示例化后的 对象作为 原型,对此原型 进行 复制,得到一个新的对象,这个新对象 和原型 的 属性等完全相同,并且 用户 可以在此新对象 基础上 进行改变(新增或修改属性); 现实生活中的例子: 1.克隆羊 多莉,对 原有 羊 进行克隆 后 得到一个 完全相同的 新对象(多莉); 2.书籍的 不同版本; 在第一版书籍 出版后,发现一些问题, 需要出版第二版,这时 作者 不会从头开始 一页一页的 写书等等, 作者会在第一版的 进行克隆 生成 第二版..

rgc_520_zyl的博客 579

秒懂设计模式之原型模式(Prototype Pattern)

概述 原型模式属于创建型模式,所以它是描述如何创建对象的模式。顾名思义, 先搞一个原型对象出来,然后在这个原型对象的基础上修修补补弄出一个新对象来。 使用场景 当一个对象的构建代价过高时。例如某个对象里面的数据需要访问数据库才能拿到,而我们要多次构建这样的对象。 当构建的多个对象,均需要某种原始状态时,就可以使用原型对象保存这种原始状态,其他对象基于原型对象来修改。 《江城子·密州出猎》 ----------------------------------------------------------

ShuSheng007的程序人生 7467

Java设计模式——原型模式【ProtoType Pattern】

Java设计模式、原型模式

scorpio的博客 496

详解Java设计模式之原型模式(Prototype Pattern)

在软件系统中,有时候需要多次创建某一类型的对象,为了简化创建过程,可以只创建一个对象,然后再通过克隆的方式复制出多个相同的对象,这就是原型模式的设计思想。

Elegance never goes out of fashion 9050

【设计模式】原型模式(Prototype Pattern)详解

摘要: 原型模式通过复制已有对象(原型)来创建新对象,避免重复初始化带来的性能开销。核心思想是“拷贝”而非重新构造,适用于创建成本高、对象差异小的场景。Java通过Cloneable接口支持原型模式,但需注意浅拷贝(仅复制对象本身)与深拷贝(复制对象及引用对象)的区别。优点包括提高性能、简化创建过程,缺点在于实现复杂且可能破坏封装性。典型应用包括Spring的prototype作用域、游戏角色复制等。使用时应权衡对象创建成本,避免过度设计。

经天纬地,格物致理 923

23种设计模式——原型模式 (Prototype Pattern)详解

本文介绍了23种经典设计模式之——原型模式 (Prototype Pattern)的说明及使用

Meteors.的博客 1001
上一篇: jQuery 的下拉列表日期控件
下一篇: 写给我们这些浮躁的程序员
datuo2821
博客等级 码龄11年 0粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值