简单工厂、工厂方法、抽象工厂

简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。

本文是本人对这三种模式学习后的一个小结以及对他们之间的区别的理解。

简单工厂

简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。

不修改代码的话,是无法扩展的。

工厂方法

工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。

在同一等级结构中,支持增加任意产品。

抽象工厂

抽象工厂是应对产品族概念的。比如说,每个汽车公司可能要同时生产轿车,货车,客车,那么每一个工厂都要有创建轿车,货车和客车的方法。

应对产品族概念而生,增加新的产品线很容易,但是无法增加新的产品。

小结

★工厂模式中,重要的是工厂类,而不是产品类。产品类可以是多种形式,多层继承或者是单个类都是可以的。但要明确的,工厂模式的接口只会返回一种类型的实例,这是在设计产品类的时候需要注意的,最好是有父类或者共同实现的接口。

★使用工厂模式,返回的实例一定是工厂创建的,而不是从其他对象中获取的。

★工厂模式返回的实例可以不是新创建的,返回由工厂创建好的实例也是可以的。

区别

简单工厂:用来生产同一等级结构中的任意产品。(对于增加新的产品,无能为力)

工厂模式 :用来生产同一等级结构中的固定产品。(支持增加任意产品)

抽象工厂:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
    
  同一等级结构:中国动物->中国猫-〉  
                                              -〉中国狗-〉藏敖  
                                                                -〉京叭     以上动物属于同一等级结构  
  同一产品族:中国狗,日本狗,美国狗则属于同一产品族。  
   
  以上三种工厂方法在等级结构和产品族这两个方向上的支持程度不同。所以要根据情况考虑应该使用哪种方法。

 

 

//另一

工厂模式(Factory Pattern)
工厂模式主要为创建对象提供了接口,工厂模式按照《java与模式》中的提法分为3类:
1、简单工厂模式(Simple Factory)
2、工厂方法模式(Factory Method)
3、抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性
还有一种分类方法就是将简单工厂模式看为工厂方法模式的一种特例,两个归为一类。
需要使用工厂模式的情况:
1、在编码时不能预见需要创建哪种类的实例;
2、系统不应该依赖于产品类实例如何被创建、组合和表达的细节。
下面开始对三种类型的工厂模式进行详细说明。
 
一、简单工厂模式
角色组成:
工厂类角色:模式核心,含有一定的商业业务逻辑和判断逻辑,往往由一个具体类实现;
抽象产品角色:一般是具体产品继承的父类或者实现的接口,往往是接口或者抽象类;
具体产品角色:工厂类所创建的对象就是这类角色的实例。
例子如下(FactoryPattern.java JDK1.5编译测试通过):
/**
 *工厂模式测试
 *@author tsimgsong
 *@version 1.0,09/04/2007
 */
public class FactoryPattern{
   public static void main(String[] args){
          Fruit f;
          try{
        f = FruitShop.buy("apples");
          f.eatMethod();
          }catch(UnAvailableProduct e){
             System.out.println("出错原因:"+e.getMessage());
          }
   }
 
}
/**
 *抽象产品角色
 */
interface Fruit{
    void eatMethod();
    void growFrom();
    }
    /**
     *具体产品角色之一
     */
    class Apple implements Fruit{
           public Apple(){
                System.out.println("You create an Apple");
          }
          public void eatMethod(){
               System.out.println("You are eating an Apple");
          }
 
        public void growFrom(){
              System.out.println("Apple tree");
        }
 
    }
       /**
        *具体产品角色之二
        */
       class Orange implements Fruit
       {  
              public Orange(){
                     System.out.println("You create an Orange");
              }
              public void eatMethod(){
                     System.out.println("You are eating an Orange");
              }
              public void growFrom(){
                     System.out.println("Orange Tree");
              }
       }
       /**
        *工厂模式核心:工厂类
        */
 
       class FruitShop
       {  
              /**
               *根据参数生成相应的产品
               *@param type 产品类型
               */
              public static Fruit buy(String type) throws UnAvailableProduct{
                     if(type.equalsIgnoreCase("apple")){
                            return new Apple();
                     }else if(type.equalsIgnoreCase("orange")){
                            return new Orange();
                     }else{
                            throw new UnAvailableProduct("尚未上市");
                     }
 
              }
       }
 
       class UnAvailableProduct extends Exception
       {
              public UnAvailableProduct(String msg){
                     super(msg);
              }
       }
 
分析:当增加一个具体产品类的时候,只要复核抽象产品定制的规则,也即实现或者继承了抽象产品类,只要在工厂类注册就可以生成使用。从具体产品的角度来看,是满足开闭原则的,对扩展开放同时对修改关闭,但从工厂的角度而言又不满足开闭开闭原则,每增加一类具体产品,都必须要修改工厂类,增加相应的商业业务逻辑和判断逻辑。同时在实际应用中,很可能产品是一个多层次的树状结构,而简单工厂模式只有一个工厂类对应这些产品,这种模式对于业务简单的时候可以满足应用,而对于复杂的业务环境可能能力不够。
 
二、工厂方法模式
角色组成:
抽象工厂角色:工厂方法模式的核心,具体工厂角色必须实现的或者继承的父类,通过抽象类或者接口来表现,在前面的简单工厂模式的工厂的基础上抽象出工厂的工厂
具体工厂角色:含有与具体业务逻辑有关的代码,由应用调用以创建具体的产品。由具体类实现
抽象产品角色:同上
具体产品角色:同上
在前面一个例子的基础上,如果商店越来越多,有的专卖水果,有的专卖蔬菜,有的专卖肉类,需要一个管理机构公司来管理,通过这个机构来生成具体的商店,由公司来指定具体的商店,在商店可以买到相应商品。
在上例的基础上修改如下:(FactoryPattern.java JDK1.5测试通过)
/**
 *工厂模式测试
 *@author tsimgsong
 *@version 1.0,09/04/2007
 */
public class FactoryPattern{
   public static void main(String[] args){
          Food f;
          Corp corp;
          try{
                 corp = new FreshShop();
        f = corp.buy("pig");
          f.eatMethod();
          }catch(UnAvailableProduct e){
             System.out.println("出错原因:"+e.getMessage());
          }
   }
 
}
    /**
     *工厂模式核心:抽象工厂角色
     */
    interface Corp {
              Food buy(String type) throws UnAvailableProduct;
       }
    /**
     *具体工厂角色:水果商店工厂类
     *
     */
       class FruitShop implements Corp
       {  
              /**
               *根据参数生成相应的产品
               *@param type 产品类型
               */
              public Food buy(String type) throws UnAvailableProduct{
                     if(type.equalsIgnoreCase("apple")){
                            return new Apple();
                     }else if(type.equalsIgnoreCase("orange")){
                            return new Orange();
                     }else{
                            throw new UnAvailableProduct("尚未上市");
                     }
              }
       }
 
       /**
        *具体工厂角色:肉类商店工厂类
        */
       class FreshShop implements Corp{
              public Food buy(String type) throws UnAvailableProduct{
                     if(type.equalsIgnoreCase("pig")){
                            return new Pig();
                     }else if(type.equalsIgnoreCase("goat")){
                            return new Goat();
                     }else{
                            throw new UnAvailableProduct("尚未上市");
                     }
              }
 
       }
 
    /**
     *抽象产品角色
     */
    interface Food{
        void eatMethod();
        void growFrom();
    }
       /**
        *具体产品角色 水果
        */
 
    class Apple implements Food{
           public Apple(){
                System.out.println("You create an Apple");
          }
          public void eatMethod(){
               System.out.println("You are eating an Apple");
          }
 
        public void growFrom(){
              System.out.println("Apple tree");
        }
 
    }
       /**
        *具体产品角色之一 水果
        */
       class Orange implements Food
       {  
              public Orange(){
                     System.out.println("You create an Orange");
              }
              public void eatMethod(){
                     System.out.println("You are eating an Orange");
              }
              public void growFrom(){
                     System.out.println("Orange Tree");
              }
       }
       /**
        *具体产品角色
        */
 
       class Pig implements Food
       {
              public Pig(){
                     System.out.println("You create an Pig");
              }
              public void eatMethod(){
                     System.out.println("You are eating an Pig Fresh");
              }
              public void growFrom(){
                     System.out.println("Pig grow From");
              }
       }
       /**
        *具体产品角色
        */
 
       class Goat implements Food
       {
              public Goat(){
                     System.out.println("You create an Goat");
              }
              public void eatMethod(){
                     System.out.println("You are eating an Goat Fresh");
              }
              public void growFrom(){
                     System.out.println("Goat grow From");
              }
       }
 
 
       class UnAvailableProduct extends Exception
       {
              public UnAvailableProduct(String msg){
                     super(msg);
              }
       }
工厂方法使用一个抽象工厂角色作为核心来代替在简单工厂模式中使用具体工厂类作为核心,当有新的产品加入时,只要按照抽象产品角色、抽象工厂角色提供的合同来完成,就可以被客户使用,而不必去修改任何已有的代码,完全符合开闭原则。
 
三、抽象工厂模式
抽象工厂模式是一种比工厂模式抽象成都更高的模式。这种模式是由一个工厂类层次和N个产品类层次组成,从每一个产品类层次中提取出一个产品类形成产品家族,这个类族的实例为产品族,产品族中的产品之间有一种依赖关系,一个具体的工厂类负责创建产品族中的各个产品。
抽象工厂模式的本意要求我们创建具体产品对象时,客户端不能暗示任何具体产品对象类型的信息,但是通过分析模式的通信接口可知,客户端可以告诉工厂类这些具体产品的父类。
关键点在于产品体系,如笔记本,有IBM的笔记本,DELL的笔记本,HP的笔记本,现在有一个抽象笔记本生产商,具体的生产商有IBMProducer,DELLProducer,HPProducer,对于笔记本都有一系列的组件,CPU,RAM,KeyBoard,每个工厂都需要生产CPU,RAM,KeyBoard。在生成具体的工厂类的时候就确定了后面要生产的CPU,RAM或者KeyBoard的具体对象。
例子如下(AbstractFactory.java JDK1.5测试通过):
/**
 *抽象工厂类示例
 *@author tsimgsong
 *@version 1.0,09/04/2007
 */
 class TestAbstractFactory{
        public static void main(String[] args){
               AbstractFactory factory = new IBMFactory();
               factory.createCPU();
        }
}
 
/**
 *抽象工厂
 */
public interface AbstractFactory{
    CPU createCPU();
       RAM createRAM();
       KeyBoard createKeyBoard();
}
 
/**
 *具体工厂 IBM ,生产IBM笔记本所需要的所有产品体系
 */
class IBMFactory implements AbstractFactory
{
        public CPU createCPU(){
               return new IBMCPU();
        }
        public RAM createRAM(){
               return new IBMRAM();
        }
        public KeyBoard createKeyBoard(){
               return new IBMKeyBoard();
        }
}
/**
 *具体工厂 DELL ,生产DELL笔记本所需要的所有产品体系
 */
class DELLFactory implements AbstractFactory
{
        public CPU createCPU(){
               return new DELLCPU();
        }
        public RAM createRAM(){
               return new DELLRAM();
        }
        public KeyBoard createKeyBoard(){
               return new DELLKeyBoard();
        }
}
/**
 *具体工厂 HP,生产HP笔记本所需要的所有产品体系
 */
class HPFactory implements AbstractFactory
{
        public CPU createCPU(){
               return new HPCPU();
        }
        public RAM createRAM(){
               return new HPRAM();
        }
        public KeyBoard createKeyBoard(){
               return new HPKeyBoard();
        }
}
 
/**
 *抽象产品类 CPU
 */
abstract class CPU
{
}
/**
 *抽象产品类 RAM
 */
abstract class RAM
{
}
/**
 *抽象产品类 KeyBoard
 */
abstract class KeyBoard
{
}
/**
 *具体产品类
 */
class IBMCPU extends CPU
{
       public IBMCPU(){
              System.out.println("new IBMCPU");
       }
}
class DELLCPU extends CPU
{
       public DELLCPU(){
              System.out.println("new DELLCPU");
       }
}
class HPCPU extends CPU
{
       public HPCPU(){
              System.out.println("new HPCPU");
       }
}
class IBMRAM extends RAM
{
       public IBMRAM(){
              System.out.println("new IBM Ram");
       }
}
class DELLRAM extends RAM
{
       public DELLRAM(){
              System.out.println("new DELL Ram");
       }
}
class HPRAM extends RAM
{
       public HPRAM(){
              System.out.println("new HP Ram");
       }
}
 
class IBMKeyBoard extends KeyBoard
{
       public IBMKeyBoard(){
              System.out.println("new IBM KeyBoard");
       }
}
class DELLKeyBoard extends KeyBoard
{
       public DELLKeyBoard(){
              System.out.println("new DELL KeyBoard");
       }
}
class HPKeyBoard extends KeyBoard
{
       public HPKeyBoard(){
              System.out.println("new HP KeyBoard");
       }
}
在生产笔记本时,只要制定正确的笔记本产生,对具体的组件就无需再传递任何信息就可以生产出正确的组件。一个具体的工厂生产的是一个能构成产品体系的产品。如果说现在需要增加一种新的产品,如笔记本同时需要生产mouse,需要修改的包括,增加一个mouse抽象类或接口,针对各个厂商实现具体的mouse实现类,修改各个抽象工厂接口,增加生产mouse的方法,修改各个具体工厂类。
在一篇网文中提出了一个改进的方案,改进方案引用如下
http://www.ojava.net/read.php?tid=7243,部分内容做了修改)
改革抽象工厂
有没有觉得要改动的地方有点多呢,下面我们来改革一下
1.      把组件工厂中的
生产文本组件();
生产标签组件();
...
都改为
生产组件(组件标识);
要做到上面的,需要做以下几件事情
1.增加一个Annotation来说明后面增加的组件注册表
@interface 组件描述 {
   Class 组件类();
  
}
2.增加一个Enum
enum 组件注册表 {
   /**
    * Linux_文本的对应实体类为 Linux文本
    */
   @组件描述(组件类 = Linux文本.class)
   Linux_文本,
  
   @组件描述(组件类 = Linux标签.class)
   Linux_标签,
  
   @组件描述(组件类 = Windows文本.class)
   Windows_文本,
  
   @组件描述(组件类 = Windows标签.class)
   Windows_标签,
}
3.我们不再需要
interface 组件工厂,class Windows标签组件工厂,class Linux组件工厂
我们把接口组件工厂改为实体类
为了保持可以扩展和维护
我们定义了一个接口工厂
interface 工厂 {
}
class 组件工厂 implements 工厂 {
   public 组件生产组件(组件注册表 ID) throws Exception {
       try {
           Field f = 组件注册表.class.getField(ID.toString());
           组件描述描述 = f.getAnnotation(组件描述.class);
           Class 组件类 = 描述.组件类();
           return (组件) 组件类.newInstance();
           // 注意,组件类.newInstance();的调用的时候要确保这个组件类有个不带参数的构造函数
           // 如果要使用带参数的构造函数,可以在@interface 组件描述中增加一个成员
           // 构造函数[] 构造函数参数() default{};
           // @interface 构造函数 {Class[] 构造函数的参数();}
           // 通过组件类.getConstructors(); 来得到这个类的不同构造方法
           // 这样就可以根据用户提供的信息用不同的构造函数实例话对象
           // 带不同的构造函数,这里先不讨论,后面我会给出代码
       } catch (Exception e) {
           throw new Exception ("没有找到对应的组件");
       }
   }
}

经过上面的修改,代码如下

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;

@Retention(RetentionPolicy.RUNTIME)
@interface 组件描述 {
   Class 组件类();
  
}

enum 组件注册表 {
   @组件描述(组件类 = Linux文本.class)
   Linux_文本,
  
   @组件描述(组件类 = Linux标签.class)
   Linux_标签,
  
   @组件描述(组件类 = Windows文本.class)
   Windows_文本,
  
   @组件描述(组件类 = Windows标签.class)
   Windows_标签,
}

interface 组件 {}

interface 文本 extends 组件 {}

interface 标签 extends 组件 {}

class Linux文本 implements 文本{
   public String toString() {
       return "Linux文本";
   }
}

class Linux标签 implements 标签{
   public String toString() {
       return "Linux标签";
   }
}

class Windows文本 implements 文本{
   public String toString() {
       return "Windows文本";
   }
}

class Windows标签 implements 标签{
   public String toString() {
       return "Windows标签";
   }
}

interface 工厂 {}

class 组件工厂 implements 工厂{
   public 组件生产组件(组件注册表 ID) throws Exception {
       try {
           Field f = 组件注册表.class.getField(ID.toString());
           组件描述描述 = f.getAnnotation(组件描述.class);
           Class 组件类 = 描述.组件类();
           return (组件) 组件类.newInstance();
       } catch (Exception e) {
           throw new Exception ("没有找到对应的组件");
       }
   }
}

class 客户系统显示 {
   private 文本 text;
   private 标签 label;
   public static void main(String args[]) {
       客户系统显示 clientOS = new 客户系统显示();
       组件工厂 factory = new 组件工厂();
       try {
           clientOS.text = (文本) factory.生产组件(组件注册表.Linux_文本);
           clientOS.label = (标签) factory.生产组件(组件注册表.Linux_标签);
       } catch (Exception e) {
           e.printStackTrace();
       }
       System.out.println(clientOS.label);
       System.out.println(clientOS.text);
   }
}

这个时候我们增加一个下拉框
需要改动的地方
1.增加一个 interface 下拉框 extends 组件 {}
2.增加2个实现类
class Windows下拉框 implements 下拉框{}
class Linux下拉框implements 下拉框{}
3.组件注册表增加2个成员
@组件描述(组件类 = Linux下拉框.class)
Linux_下拉框,   
@组件描述(组件类 = Windows下拉框.class)
Windows_下拉框,
和上面的比起来我们只需要在组件注册表中增加2个成员,而不需要去修改
1.组件工厂
2.Linux组件工厂
3.Windows标签组件工厂
因为这里要修改3个地方,是不是觉得麻烦,反正我觉得麻烦了点
还有一点就是用户调用的时候不需要再使用factory.生产标签组件();等方法,只要一个factory.生产组件就可以了,这样符合简单工厂的模式


第三部分带参数的构造函数代码

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@Retention(RetentionPolicy.RUNTIME)
@interface 构造函数 {
   /**
    * 构造函数的参数类型
    * @return
    */
   Class[] 构造函数的参数();
}

@Retention(RetentionPolicy.RUNTIME)
@interface 组件描述 {
   Class 组件类();
  
   /**
    * 返回组件的构造函数 <br>
    * 如果长度为0,则调用没有参数的构造函数 <br>
    * @return 构造函数[]
    */
   构造函数[] 构造函数参数() default{};
  
}

enum 组件注册表 {
   /**
    * Linux_文本的对应实体类为 Linux文本 <br>
    * Linux的构造函数有 <br>
    * 1. Linux文本(String 显示的文字) ; <br>
    * 2. Linux文本(String 显示的文字, Integer 文本字体大小);
    */
   @组件描述(组件类 = Linux文本.class,
           构造函数参数 = {@构造函数(构造函数的参数={String.class}) ,
       @构造函数(构造函数的参数={String.class, Integer.class}) } )
   Linux_文本,
  
   @组件描述(组件类 = Linux标签.class)
   Linux_标签,
  
   @组件描述(组件类 = Windows文本.class)
   Windows_文本,
  
   @组件描述(组件类 = Windows标签.class)
   Windows_标签,
}

interface 组件 {}

interface 文本 extends 组件 {}

interface 标签 extends 组件 {}

class Linux文本 implements 文本{
   private String text;
   private Integer size;
   public Linux文本(String text) {
       this.text = text;
   }
   public Linux文本(String text, Integer size) {
       this.text = text;
       this.size = size;
   }
   public String toString() {
       return "Linux文本" + (text == null ? "":",文本内容为:"+text) + (size == null ? "":",文本字体大小为:"+size);
   }
}

class Linux标签 implements 标签{
   public String toString() {
       return "Linux标签";
   }
}

class Windows文本 implements 文本{
   public String toString() {
       return "Windows文本";
   }
}

class Windows标签 implements 标签{
   public String toString() {
       return "Windows标签";
   }
}

interface 工厂 {}

class 组件工厂 implements 工厂{
   public 组件生产组件(组件注册表 ID, Object[] 参数) throws Exception {
       try {
           Field f = 组件注册表.class.getField(ID.toString());
           组件描述描述 = f.getAnnotation(组件描述.class);
           Class 组件类 = 描述.组件类();
           构造函数[] ano = 描述.构造函数参数();
           if (参数 != null) {
               for (int i = 0; i < ano.length; i++) {
                   构造函数 temp = ano;
                   Class[] 构造函数S = temp.构造函数的参数();                   
                   if (参数.length == 构造函数S.length) {
                       for (int j = 0; j < 参数.length; j++) {
                           if (参数[j].getClass().toString().equals(构造函数S[j].toString())) {
                               if ( j == 参数.length - 1) {
                                   Constructor cons = 组件类.getConstructor(构造函数S);
                                   return (组件) cons.newInstance(参数);
                               }
                           } else break;
                       }
                   }
                   continue;
               }
               throw new Exception ("没有找到对应的组件");
           } else
               return (组件) 组件类.newInstance();
       } catch (Exception e) {
           e.printStackTrace();
           throw new Exception ("没有找到对应的组件");
       }
   }
}

class 客户系统显示 {
   private 文本 text;
   private 标签 label;
   public static void main(String args[]) {
       客户系统显示 clientOS = new 客户系统显示();
       组件工厂 factory = new 组件工厂();
       try {
           Object [] params = {"初始化文本", new Integer(20)};
           clientOS.text = (文本) factory.生产组件(组件注册表.Linux_文本,params);
           clientOS.label = (标签) factory.生产组件(组件注册表.Linux_标签,null);
           System.out.println(clientOS.label);
           System.out.println(clientOS.text);
           Object [] params2 = {"初始化"};
           clientOS.text = (文本) factory.生产组件(组件注册表.Linux_文本,params2);
           System.out.println(clientOS.text);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}
 
ref:
1、 http://www.jdon.com/designpatterns/designpattern_factory.htm
2、 http://www.pcworld.com.cn/how_to_use/1/2006/0714/6926.shtml
3、 http://java.chinaitlab.com/model/3042.html
4、 http://www.ojava.net/read.php?tid=7243


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tsimgsong/archive/2007/09/05/1772534.aspx

 

 

 

//其它

假想场景:hp和apple是全球知名的电脑生产厂家,他们各自的电脑操作系统分别是windows和macintosh,microsoft是软件行业的龙头。为了吸引更多客户购买电脑,hp和apple请ms为他们开发两款最常用的软件,办公软件和及时通讯工具。ms结了订单后,分别为hp和apple开发了office和msn。

上面的场景其实蕴含着我们常见的设计模式思维方式,code is cheap,代码来了。

一:简单工厂

上面的简单工厂在一个方法体中有个switch...case,违背了“对扩展开放,对修改封闭”的原则,即“开放-封闭原则”。下面的工厂方法就克服了简单工厂的缺点,降低了客户端程序和产品对象的耦合。

二:工厂方法
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

工厂方法的缺点是每加一个产品,就要创建一个对应的产品工厂类,增加了额外的开发量。

三、抽象工厂

提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂最经典的应用莫过于数据库访问程序。网上有很多这方面的讨论。不多说了。这里我继续用开篇讲的场景来描述。
我们知道office和msn根据操作系统的不同可以分为windows版本的和mac版本的,也许还有linux,unix版本的,归根结底,这里的产品涉及到产品系列的问题,抽象工厂可以解决这个问题:

转自:http://www.cnblogs.com/wjfluisfigo/archive/2009/03/15/1411621.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值