Simple Factory Vs Factory Method Vs Abstract Factory by Example

http://vivekcek.wordpress.com/2013/03/17/simple-factory-vs-factory-method-vs-abstract-factory-by-example/

http://blog.csdn.net/humanbeng/article/details/7176751


Simple Factory Pattern

Definition:
Creates objects without exposing the instantiation logic to the client.
Refers to the newly created object through a common interface

Diagram:



Explanation:

The heart of above Simple Factory pattern is the ‘MobileFactory’ class. From the Client when we create an instance of this ‘MobileFactory’, this class will load and cache all the classes that implement the ‘IMobile’ interface by using reflection or some other logic. After that we can call the ‘GetMobile(string Name)’ method in the ‘MobileFactory’ class which will return the specified object through the parameter.

The Client will expect an object that implements the ‘IMobile’ interface.

Factory Method

Definition:

Defines an interface for creating objects, but let subclasses to decide which class to instantiate
Refers the newly created object through a common interface.

Diagram:



Explanation:

In Factory Method pattern we will introduce a new interface called ‘IMobileFactory’ and two concrete implementation’s ‘NokiaFactory’ and ‘IphoneFactory’. These concrete classes control the object creation.

In my example the client want a Nokia object. So the steps are given below.

1.The client will load a reference to ‘NokiaFactory’. But Client won’t refer the ‘NokiaFactory’ class directly like the Simple Factory pattern. The client refers the concrete implementation through an interface ‘IMobileFactory’.

2.Then the Client call the ‘CreateMobile()’ method that will return an object of type ‘IMobile’.

3.Here we have to inform the client the concrete implementation to be used through some configuration or parameters.

注意:

IMobile factory = new NokiaFactory();

IMobile factory = new IphoneFactory();

这样的形式也是对的


Abstract Factory

Definition:

Abstract Factory offers the interface for creating a family of related objects, without explicitly specifying their classes

Diagram:


Explanation:

The main point regarding Abstract Factory pattern is that, this pattern creates a family of related objects that have different parent class or interface.

In Abstract Factory pattern the object creation happens in the same way as Factory Method. The only difference is the creation of related objects.

I now updated our old ‘IMobileFactory’ interface with two new methods.
CreateNokiaMobile() – Returns a Nokia objects that implements an ‘INokia’ interface.
CreateAppleMobile()- Returns Iphone objects that implements ‘IApple’ interface.

There are two concrete implementation of ‘IMobileFactory’ named 3GMobileFactory and 4GMobileFactory.

3GMobileFactory – Can return 3G supported mobiles of Nokia and Apple. So as per definition confirms, which returns a family of related objects that have different parents (INokia, IApple).

4GMobileFactory – Can return 4G supported mobiles of Nokia and Apple

注意:抽象工厂和工厂方法的区别在于,工厂方法创建返回的是一个具体的产品,而抽象工厂返回的是一类产品,也就是产品的接口


简单工厂模式
生活例子
吃饭是人的基本需求,如果人类不需要吃饭,可能我们就能活得清闲许多,也就不需要像现在一样没日没夜的工作,学习。我们学习是为了找到更好的工作,好工作 为了赚更多的钱,最终为了吃饱饭,吃好饭。因此可以说吃饭是与人息息相关,下面就从吃饭的例子来引入工厂模式的学习。

   如果你想吃饭了,怎么办自己做吗?自己做就相当于程序中直接使用new。当然是自己下个指令,别人来做更爽。那就把做饭的任务交给你的老婆吧,那么她就是 一个做饭的工厂了,你告诉她要要吃红烧肉,等会她就从厨房给你端出来一盘香喷喷的红烧肉了,再来个清蒸鱼吧,大鱼大肉不能太多,那就再来个爆炒空心菜,最 后再来个西红柿鸡蛋汤。下图 1) 就是这个问题的模型。
 
(图1)
显然到了这里,你是Client,你老婆就是工厂,她拥有做红烧肉的方法,做清蒸鱼的方法,做爆炒空心菜、西红柿鸡蛋汤的方法,这些方法返回值就是食物 抽象。红烧肉、清蒸鱼、爆炒空心菜、西红柿鸡蛋汤就是食物的继承类,到这里你就可以大吃二喝了。简单工厂模式也成型了。哈哈,娶一个手艺不错的老婆还真 好,吃的好,吃的爽,又清闲。

   下面来看标准的简单工厂模式的分析。 
意图
把一系列拥有共同特征的产品的创建封装
结构图

(图2)
角色分析
产品基类: 工厂创建的所有产品的基类, 它负责描述所有实例所共有的公共接口。它用来作为工厂方法的返回参数。
代码实现:
        //---这时一个系列的产品基类
class Product
{
protected:
Product(void);
public:
virtual ~Product(void);
public:
virtual void Function() = 0;
};
//cpp
Product::Product(void)
{
}
Product::~Product(void)
{
}
具体产品类:产品1和产品2,这个角色实现了抽象产品角色所定义的接口。
代码实现:
        //产品A
class ConcreteProductA:public Product
{
public:
ConcreteProductA(void);
public:
virtual ~ConcreteProductA(void);
public:
virtual void Function();
};
//cpp
ConcreteProductA::ConcreteProductA()
{
cout<<"创建 A 产品"<<endl;
}
ConcreteProductA::~ConcreteProductA()
{
cout<<"释放 A 产品"<<endl;
}
void ConcreteProductA::Function()
{
cout<<"这是产品 A 具有的基本功能"<<endl;
}
//产品B与A类似不这里不再给出,大家可以下载源码

工厂类:负责具体产品的创建,有两种方式实现产品的创建,I、创建不同的产品用不同的方法;II、创建不同产品用相同的方法,然后通过传递参数实现不同产品的创建。本实例中两种模式都给出了,大家自行分析。
//简单工厂,此类不需要继承,直接硬编码实现生成的产品
class SimpleFactory
{
public:
SimpleFactory(){}
public:
~SimpleFactory(){}
public:
Product *CreateProduct(int ProuctType);
Product *CreateProductA();
Product *CreateProductB();
};
//CPP
Product * SimpleFactory::CreateProduct(int ProductType=0)
{
Product *p = 0;
switch(ProductType)
{
case 0:
p= new ConcreteProductA();
break;
case 1:
p= new ConcreteProductB();
break;
default:
p= new ConcreteProductA();
break;
}
return p;
}
Product *SimpleFactory::CreateProductA()
{
return new ConcreteProductA();
}
Product *SimpleFactory::CreateProductB()
{
return new ConcreteProductB();
}
客户端程序:访问的角色包括产品基类、工厂类。不直接访问具体产品类。通过基类指针的多态实现产品功能的调用。
访问描述:客户程序通过调用工厂的方法返回抽象产品,然后执行产品的方法。
//调用代码
SimpleFactory sf;
Product *p = sf.CreateProductA();
p->Function();
delete p;
p = sf.CreateProductB();
p->Function();
delete p;
优缺点说明
优点:1) 首先解决了代码中大量New的问题。为何要解决这个问题,好处的说明我想放到结尾总结中。
   2) 用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活。 
缺点:对修改不封闭,新增加产品您要修改工厂。违法了鼎鼎大名的开闭法则(OCP)。
附加说明
  • 大家可以参看 吕震宇 老师的C#设计模式(四)参看这个模式的分析,里面还给出了这个模式的两个变体,实现比较简单,有兴趣的朋友可以自行用C++实现一下。
  • 产 品基类的代码中构造函数我用了Protected,而没有使用Public,主要是为了体现编码中的一个最小权限原则。说明此类不许用户直接实例化。虽然 这里使用了virtual void Function() = 0;编译器也会控制不让用户直接实例化,不过我依然认为使用私有化构造函数来保护类不直接实例化是一个良好的编程风格。


工厂方法模式
生活例子:
   人是最贪得无厌的动物,老婆手艺再好,总有不会做的菜,你想吃回锅肉,怎么办,让老婆学呗,于是就给她就新增了做回锅肉的方法,以后你再想吃一个新菜,就 要给你老婆新加一个方法,显然用老婆做菜的缺点也就暴露出来了,用程序设计的描述就是对修改永远不能封闭。当然优点也是有的,你有了老婆这个工厂,这些菜 不用你自己做了,只要直接调用老婆这个工厂的方法就可以了。

   面对上面对修改不能封闭的问题,有没有好的解决方案吗,如果你有钱,问题就迎刃而解了,把老婆抽象变成一个基类,你多娶几个具体的老婆,分别有做鱼 的,做青菜的,炖汤的老婆,如果你想吃一个新菜,就再新找个女人,从你的老婆基类继承一下,让她来做这个新菜。显然多多的老婆这是所有男人的梦想,没有办 法,法律不允许,那么咱们只是为了做饭,老婆这个抽象类咱们不叫老婆了,叫做厨师吧,她的子类也自然而然的该叫做鱼的厨师、炖汤的厨师了。现在来看这个模 式发生了变化,结构中多了一个厨师的抽象,抽象并不具体的加工产品了,至于是炖汤还是炖鱼,是由这个抽象工厂的继承子类来实现,现在的模式也就变成工厂方 法模式了,这个上面的结构图1)就变成了下面的图3的结构了。
 
   (图3)

   现在再来分析现在的模式,显然简单工厂的缺陷解决了,新增加一个菜只需要新增加一个厨师就行了,原来的厨师还在做原来的工作,这样你的设计就对修改封 闭了。你看把老婆解放出来,招聘大量的厨师到你家里这个方案多么的完美,你老婆也会爱死你了。当然前提就是你要有多多的钱噢,当然这里的钱的多少在软件领 域应该看你的客户软件投资方的要求。
下面来一下标准的工厂模式的实现
意图
  • 定义一个用户创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
  • 上面是GOF关于此模式的意图描述,我想补充的是您可以这样理解:为了改善简单工厂对修改不能关闭的问题。
结构


图4
角色分析
产品基类:同简单工厂的产品基类,其实就是用和简单工厂中的是同一个类,这里并没有重写。
具体产品类:也是用的简单工厂的具体产品类,为了体现对修改的关闭这里为系统新添加了一个具体产品类,就是“新产品”,代码中叫做“ConcreteProductANew”
工厂基类:定义了工厂创建产品的接口,但是没有实现,具体创建工作由其继承类实现。
代码实例 
//工厂模式,此模式的工厂只定义加工产品的接口,具体生成交予其继承类实现
//只有具体的继承类才确定要加工何种产品
class Factory
{
public:
Factory(void);
public:
virtual ~Factory(void);
public:
virtual Product* CreateProduct(int ProductType = 0) =0;
};
//CPP
Factory::Factory(void)
{
}
Factory::~Factory(void)
{
}
具体工厂类:工厂基类的具体实现,由此类决定创建具体产品,这里 ConcreteFactory1对于与图中的 工厂实现ConcreteFactory2 对于与图中的新工厂
下面给出实现代码
//工厂实现
class ConcreteFactory1:public Factory
{
public:
ConcreteFactory1();
public:
virtual ~ConcreteFactory1();
public :
Product* CreateProduct(int ProductType);
};
//新工厂,当要创建新类是实现此新工厂
class ConcreteFactory2:public Factory
{
public:
ConcreteFactory2();
public:
virtual ~ConcreteFactory2();
public :
Product* CreateProduct(int ProductType);
};
//CPP
ConcreteFactory1::ConcreteFactory1()
{
}
ConcreteFactory1::~ConcreteFactory1()
{
}
Product * ConcreteFactory1::CreateProduct(int ProductType = 0)
{
Product *p = 0;
switch(ProductType)
{
case 0:
p= new ConcreteProductA();
break;
case 1:
p= new ConcreteProductB();
break;
default:
p= new ConcreteProductA();
break;
}
return p;
}
ConcreteFactory2::ConcreteFactory2()
{
}
ConcreteFactory2::~ConcreteFactory2()
{
}
Product * ConcreteFactory2::CreateProduct(int ProductType = 0)
{
return new ConcreteProductANew();
}
客户端调用:访问角色(产品基类、工厂基类、工厂实现类)
调用描述:客户程序通过工厂基类的方法调用工厂实现类用来创建所需要的具体产品。从而实现产品功能的访问。
代码实现
Factory*fct = new ConcreteFactory1();
Product *p = fct->CreateProduct(0);
p->Function();
delete p;
p = fct->CreateProduct(1);
p->Function();
delete p;
delete fct;
fct = new ConcreteFactory2();
p=fct->CreateProduct();
delete p;
delete fct;
优缺点分析
优点
  • 简单工厂具有的优点
  • 解决了简单工厂的修改不能关闭的问题。系统新增产品,新增一个产品工厂即可,对抽象工厂不受影响。
缺点:对于创建不同系列的产品无能为力
适用性
  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
其他参考

抽象工厂模式
生活例子
    世事多变,随着时间的推移,走过的地方越来越多,你天南海北的朋友也越来越多。你发现菜原来还分了许多菜系,鲁菜、粤菜、湘菜等等,它们各有各的风味,同 样是红烧肉由不同菜系出来的味道也各不相同, 你招待不同的朋友要用不同的菜系,这下难办了,你的厨师都是鲁菜风味,怎么办,广东的朋友来了吃不惯。现在我们再回到简单工厂模式(就是老婆做菜的模 式),我们把红烧肉再向下继承,生成鲁菜红烧肉、粤菜红烧肉、湘菜红烧肉;清蒸鱼向下继承为鲁菜清蒸鱼、粤菜清蒸鱼、湘菜清蒸鱼,其它也以此类推。我们也 修改一下老婆的这个类,不让其返回食物基类,而是返回红烧肉、清蒸鱼、爆炒空心菜、西红柿鸡蛋汤这一层次,并把这些方法抽象化,作为菜系工厂基类,然后再 从此基类继承出,鲁菜工厂、粤菜工厂、湘菜工厂等等,再由这些具体工厂实现创建具体菜的工作,哈哈你如果招待广东朋友就用粤菜工厂,返回的就是一桌粤菜菜 系的红烧肉、清蒸鱼、空心菜和西红柿鸡蛋汤了,你的广东朋友一定会吃的非常合乎胃口了。噢,非常好,你已经实现了抽象工厂模式了。结构模型图也变成了下图 6)的样子了。

(图6)

       现在可以看到,想新来做一个菜系,只需新聘请一个厨师就可以了,多么完美,但是你先别高兴太早,如果你想新增加一个菜就变得非常困难了。

比如要增加一个湘菜的红烧肉,首先湘菜红烧肉继承红烧肉类,然后再定义个湘菜厨子,他返回的就是湘菜红烧肉了

但是,如果要增加一个叫干锅娃娃菜的产品,厨师接口、鲁菜厨子和粤菜厨子都要增加一个 “加工干锅娃娃菜”的接口或者函数


简单工厂严格说并不是一个设计模式。简单工厂没有抽象类,只有一个具体工厂类如MyFactory,然后MyFactory里面有个工厂方法CreateProduct返回一个基类产品,具体返回什么具体实例通过传入参数然后用case判断。

用手机生产做个例子:

比如Nokia简单工厂就是只有NokiaFactory工厂类,工厂方法就是CreateNokiaPhone,参数是Nokia手机型号,根据不同型号创建不同的Nokia手机(使用case)。很明显的缺点就是Nokia每发明一个新型号的手机都需要修改简单工厂类(增加case判断),违反了封闭修改,开放扩展原则。

工厂方法:该模式有一个抽象基类和若干个派生的具体工厂类,基类定义了一个虚工厂方法返回指定产品类的基类,派生类需要实现该虚方法并创建具体产品类返回。注意工厂方法的每个具体工厂只负责返回一种产品类。

同样以手机生产做例子:

Nokia工厂方法模式有一个工厂基类NokiaFactory,注意此工厂和上面不一样,是抽象的。该类定义一个虚工厂方法CreateNokiaPhone,该方法返回NokiaPhone基类。然后不同型号的手机对应一个该型号的手机工厂,比如N97Factory,注意此工厂派生于NokiaFactory基类,N97Factory实现虚工厂方法,它返回值是具体的Nokia手机类,如new N97Phone。(注意N97Phone是NokiaPhone的派生类这样的优点就是,新出一个Nokia手机型号,只需派生一个该型号的工厂而无需修改原来的代码。符合封闭修改,开放扩展原则。

抽象工厂:该模式和工厂方法模式很相似,也是一个抽象基类和若干个具体工厂类,不同的是抽象工厂的工厂基类定义了多个虚工厂方法,每个虚工厂方法负责返回一种产品,多个工厂方法返回多种产品,并且这些产品具有某些联系。

抽象工厂就不止生产Nokia手机了,还生产相关的周边产品,首先NokiaFactory基类会定义两个虚方法,一个是CreateNokiaPhone用来生产手机,另一个是CreateNokiaCharger用于生产对应型号的手机充电器(假定不同型号手机使用不同的充电器)。然后派生类N97Factory就要生产两种相关的产品,一个是N97Phone,还有一个是N97Charger,这两个对象是对应的关系,因此封装到抽象工厂来生产一系列相关的对象

总结:用最简单的话来概括就是,简单工厂只有一个具体工厂类来创建一种基类多个不同派生类,工厂方法就是有多个派生于一个基类的具体工厂类,每个具体工厂只生产一种基类的一个派生类,抽象工厂也是只有一个工厂基类,但是每个具体工厂生产多个相关基类各一个派生类


简单工厂不是一种设计模式,在其内部通过if 或者case的方式,根据传入的参数创建相应的产品对象,但是缺点是,要是新添一个产品,就要修改if或者case


工厂方法 每个一个具体的工厂只创建一种产品的对象,新添的产品,就只需要再定义一个具体工厂类就可以了,但是不能解决创建多种产品的需求。


抽象工厂 可以创建多种产品对象,但是如果要新添一种产品(注意,如果要新添属于某种产品的产品是比较容易的),就要涉及到很多类的修改。

//

意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 
结构

角色分析
产品基类:这里包含产品基类A和产品基类B,实际上在我的示例代码中,这两个产品都从共同的基类继承而来,但是这个继承关系却是在这个模式之外的部分,而本身这个模式关心的是这两个产品基类的差异部分。
代码实现:这里的代码就是借用的简单工厂模式中具体产品类的代码实现部分,为了大家阅读方便,下面重新给出一下。
        //产品A
class ConcreteProductA:public Product
{
public:
ConcreteProductA(void);
public:
virtual ~ConcreteProductA(void);
public:
virtual void Function();
};
//cpp
ConcreteProductA::ConcreteProductA()
{
cout<<"创建 A 产品"<<endl;
}
ConcreteProductA::~ConcreteProductA()
{
cout<<"释放 A 产品"<<endl;
}
void ConcreteProductA::Function()
{
cout<<"这是产品 A 具有的基本功能"<<endl;
}
//产品B与A类似不这里不再给出,大家可以下载源码


具体产品
类:这里的具体产品类是产品A1,A2,B1、B2等,
代码实现:A1对应的实现就是“”
class ConcreteProductA1:public ConcreteProductA
{
public:
ConcreteProductA1(void);
public:
virtual ~ConcreteProductA1(void);
public:
virtual void Function();
};
//CPP
ConcreteProductA1::ConcreteProductA1()
{
cout<<"创建 A1 产品"<<endl;
}
ConcreteProductA1::~ConcreteProductA1()
{
cout<<"释放 A1 产品"<<endl;
}
void ConcreteProductA1::Function()
{
cout<<"这时产品 A1 具有的基本功能"<<endl;
}

工厂抽象接口:定义了创建产品的接口,这里返回参数是返回的产品A,产品B,而本身产品A和B的共同基类,小弟认为正是这个特征构成了抽象工厂和工厂模式的区别。
代码实现
//抽象工厂模式
class AbstractFactory
{
public:
AbstractFactory();
public:
virtual ~AbstractFactory();
public:
virtual ConcreteProductA* CreateA() = 0;
virtual ConcreteProductB* CreateB() = 0;
};
//CPP
AbstractFactory::AbstractFactory()
{
}
AbstractFactory::~AbstractFactory()
{
}

具体工厂实现类:工厂1和工厂2。新增加系列,只需新实现一个工厂。
代码实现: 工厂1的就是ConcreteAbsFactory1,工厂2的代码类似,这里没有给出,可以在下载代码中看到
工厂1-----
class ConcreteAbsFactory1:public AbstractFactory
{
public:
ConcreteAbsFactory1();
public:
virtual ~ConcreteAbsFactory1();
public:
virtual ConcreteProductA* CreateA();
virtual ConcreteProductB* CreateB();
};
//CPP
ConcreteAbsFactory1::ConcreteAbsFactory1()
{
}
ConcreteAbsFactory1::~ConcreteAbsFactory1()
{
}
ConcreteProductA* ConcreteAbsFactory1::CreateA()
{
return new ConcreteProductA1();
}
ConcreteProductB * ConcreteAbsFactory1::CreateB()
{
return new ConcreteProductB1();
}

客户端访问: 访问角色(产品基类、抽象工厂、具体工厂实现类)
访问描述: 通过抽象工厂的指针访问具体工厂实现来创建对应系列的产品,然后通过产品基类指针访问产品功能。
调用代码:
          AbstractFactory *absfct = new ConcreteAbsFactory1();
ConcreteProductA *cpa = absfct->CreateA();
cpa->Function();
delete cpa;
ConcreteProductB *cpb = absfct->CreateB();
cpb->Function();
delete cpb;
delete absfct;
absfct = new ConcreteAbsFactory2();
cpa = absfct->CreateA();
cpa->Function();
delete cpa;
cpb = absfct->CreateB();
cpb->Function();
delete cpb;

和工厂模式的分析比较
   现在可以和工厂模式对比一下,抽象工厂返回的接口不再是产品A和产品B的共同基类Product了,而是产品A、产品B基类(在工厂模式中它们为具体实现 类,这里变成了基类)了。此时工厂的抽象和简单工厂中的工厂方法也很类似,就是这些特征区使其别于工厂模式而变成抽象工厂模式了,因此抽象工厂解决的是创 建一系列有共同风格的产品(鲁菜还是粤菜),而工厂方法模式解决的创建有共同特征的一系列产品(红烧肉、清蒸鱼它们都是食物)。当然简单工厂的缺陷在抽象 工厂中又再次出现了,我要新增加一个产品,工厂抽象接口就要改变了。因此抽象工厂并不比工厂模式完美,只不过是各自的适用领域不同而已。其实,这里如果把 抽象工厂模式的接口返回产品A和产品B的共同基类(工厂模式返回的参数),你会发现,奇怪这个模式怎么这么眼熟,它不是恰恰退化成工厂模式了。
类模式与对象模式的区别讨论:先看定义类“模式使用继承关系,把对象的创建延迟的子类,对象模式把对象的创建延迟到另一个对象中”。 分析:首先它们创建对象都不是在基类中完成,都是在子类中实现,因此都符合类模式的概念;但是工厂模式的创建产品对象是在编译期决定的,要调用某个工厂固 定的,而抽象工厂模式对产品的创建是在运行时动态决定的,只有到运行时才确定要调用那个工厂,调用工厂随运行环境而改变。(这里我一直很混乱,欢迎大家讨 论)
适用性
  • 一个系统要独立于它的产品的创建、组合和表示时
  • 一个系统要由多个 产品系列中的一个来配置时
  • 当你要强调一个系列相关的产品对象的设计以便进行联合使用时
  • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
参考
总结
   工厂本质就是用工厂方法替代直接New来创建对象。这里不是指的让用户重载一个新操作符号来进行创建对象的操作,而是说把New 操作封装在一个方法中,等用户需要创建对象时调用此方法而避免直接使用New而已。这样做的目的就是之一就是封装,避免代码中大量New的运算符,这当然 不是主要目的,因为这样虽然New少了,CreateObject方法却多了,但是如果产品类的构造函数变了,我想常用工厂模式的修改源代码的工作应该简 便许多吧,当然这算不上这个模式的好处,它的真正强大的功能其实在于适应变化,这也是整个设计模式最根本的目的;还有一点就是体现了抽象于实现的分离,当 然创建型模式都具有这个特点,工厂模式非常明显吧了,把具体创建工作放置到工厂中,使客户端程序更专注与业务逻辑的,这样的代码结构也更进行合理。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值