20、深入探索 Groovy 构建器:从 Swing 到自定义构建

深入探索 Groovy 构建器:从 Swing 到自定义构建

在编程世界中,构建器(Builder)是一种强大的工具,它能让我们以简洁优雅的方式完成复杂的任务。Groovy 作为一门功能强大的动态语言,提供了丰富的构建器,不仅适用于 XML 结构,还能用于创建 Swing 应用程序、自定义任务列表等。下面将深入介绍 Groovy 构建器的使用方法和技巧。

1. 构建 Swing 应用程序

构建器的优雅之处不仅体现在 XML 结构上,Groovy 还提供了用于创建 Swing 应用程序的构建器。在使用 Swing 时,通常需要执行一些繁琐的任务,如创建组件(如按钮)、注册事件处理程序等。而 Groovy 的 SwingBuilder 结合闭包,极大地简化了这些操作。

以下是一个使用 SwingBuilder 创建简单 Swing 应用程序的示例代码:

bldr = new groovy.swing.SwingBuilder()
frame = bldr.frame(
    title: 'Swing',
    size: [50, 100],
    layout: new java.awt.FlowLayout(),
    defaultCloseOperation: javax.swing.WindowConstants.EXIT_ON_CLOSE
) {
    lbl = label(text: 'test')
    btn = button(text: 'Click me', actionPerformed: {
        btn.text = 'Clicked'
        lbl.text = "Groovy!"
    })
}
frame.show()

上述代码通过 SwingBuilder 创建了一个 JFrame 窗口,并在其中添加了一个标签和一个按钮。当点击按钮时,按钮的文本会变为 “Clicked”,标签的文本会变为 “Groovy!”。与 Java 相比,使用 SwingBuilder 可以用更少的代码完成相同的功能,提高了开发效率。

除了 SwingBuilder ,Groovy 还提供了其他相关的构建器,如 SwingXBuilder JideBuilder GraphicsBuilder ,它们分别适用于不同的 UI 库和图形处理场景。

2. 使用元编程创建自定义构建器

当处理特定的复杂任务时,如果没有现成的构建器可用,我们可以使用 Groovy 的元编程能力创建自定义构建器。下面以创建一个待办事项列表构建器为例进行说明。

首先,我们来看一下如何使用这个构建器:

bldr = new TodoBuilder()
bldr.build {
    Prepare_Vacation (start: '02/15', end: '02/22') {
        Reserve_Flight (on: '01/01', status: 'done')
        Reserve_Hotel(on: '01/02')
        Reserve_Car(on: '01/02')
    }
    Buy_New_Mac {
        Install_QuickSilver
        Install_TextMate
        Install_Groovy {
            Run_all_tests
        }
    }
}

上述代码使用自定义的 TodoBuilder 构建了一个待办事项列表。运行该代码后,输出结果如下:

To-Do:
- Prepare Vacation [start: 02/15 end: 02/22]
x Reserve Flight [on: 01/01]
- Reserve Hotel [on: 01/02]
- Reserve Car [on: 01/02]
- Buy New Mac
- Install QuickSilver
- Install TextMate
- Install Groovy
- Run all tests

接下来,我们看一下 TodoBuilder 的实现代码:

class TodoBuilder {
    def level = 0
    def result = new StringWriter()

    def build(closure) {
        result << "To-Do:\n"
        closure.delegate = this
        closure()
        println result
    }

    def methodMissing(String name, args) {
        handle(name, args)
    }

    def propertyMissing(String name) {
        Object[] emptyArray = []
        handle(name, emptyArray)
    }

    def handle(String name, args) {
        level++
        level.times { result << " " }
        result << placeXifStatusDone(args)
        result << name.replaceAll("_", " ")
        result << printParameters(args)
        result << "\n"
        if (args.length > 0 && args[-1] instanceof Closure) {
            def theClosure = args[-1]
            theClosure.delegate = this
            theClosure()
        }
        level--
    }

    def placeXifStatusDone(args) {
        args.length > 0 && args[0] instanceof Map &&
                args[0]['status'] == 'done' ? "x " : "- "
    }

    def printParameters(args) {
        def values = ""
        if (args.length > 0 && args[0] instanceof Map) {
            values += " ["
            def count = 0
            args[0].each { key, value ->
                if (key == 'status') return
                count++
                values += (count > 1 ? " " : "")
                values += "${key}: ${value}"
            }
            values += "]"
        }
        values
    }
}

TodoBuilder 中,使用了 methodMissing() propertyMissing() 方法来处理未知的方法和属性调用。当调用一个不存在的方法或属性时,会将其视为一个待办事项,并递归处理嵌套的任务。

3. 使用 BuilderSupport 创建构建器

如果需要创建多个构建器,可能会将一些方法识别代码重构到一个公共的基类中。Groovy 提供的 BuilderSupport 类就是这样一个基类,它提供了方便的方法来识别节点结构。

以下是一个使用 BuilderSupport 创建待办事项列表构建器的示例代码:

class TodoBuilderWithSupport extends BuilderSupport {
    int level = 0
    def result = new StringWriter()

    void setParent(parent, child) {}

    def createNode(name) {
        if (name == 'build') {
            result << "To-Do:\n"
            return 'buildnode'
        } else {
            return handle(name, [:])
        }
    }

    def createNode(name, Object value) {
        throw new Exception("Invalid format")
    }

    def createNode(name, Map attribute) {
        handle(name, attribute)
    }

    def createNode(name, Map attribute, Object value) {
        throw new Exception("Invalid format")
    }

    def propertyMissing(String name) {
        handle(name, [:])
        level--
    }

    void nodeCompleted(parent, node) {
        level--
        if (node == 'buildnode') {
            println result
        }
    }

    def handle(String name, attributes) {
        level++
        level.times { result << " " }
        result << placeXifStatusDone(attributes)
        result << name.replaceAll("_", " ")
        result << printParameters(attributes)
        result << "\n"
        name
    }

    def placeXifStatusDone(attributes) {
        attributes['status'] == 'done' ? "x " : "- "
    }

    def printParameters(attributes) {
        def values = ""
        if (attributes.size() > 0) {
            values += " ["
            def count = 0
            attributes.each { key, value ->
                if (key == 'status') return
                count++
                values += (count > 1 ? " " : "")
                values += "${key}: ${value}"
            }
            values += "]"
        }
        values
    }
}

TodoBuilderWithSupport 类继承自 BuilderSupport ,需要实现 setParent() createNode() 等方法。 createNode() 方法有多个重载版本,分别处理不同的参数情况。在 createNode() 方法中,根据节点名称和属性创建相应的节点,并返回节点名称。 nodeCompleted() 方法在节点处理完成时被调用,可以在该方法中进行一些最终操作。

4. 使用 FactoryBuilderSupport 创建构建器

当处理多个明确定义的节点时,使用 BuilderSupport 可能会使 createNode() 方法变得复杂。而 FactoryBuilderSupport 则基于抽象工厂模式,根据节点名称将节点创建委托给不同的工厂,使代码更加清晰和易于维护。

以下是一个使用 FactoryBuilderSupport 创建机器人构建器的示例代码:

class RobotBuilder extends FactoryBuilderSupport {
    {
        registerFactory('robot', new RobotFactory())
        registerFactory('forward', new ForwardMoveFactory())
        registerFactory('left', new LeftTurnFactory())
    }
}

class Robot {
    String name
    def movements = []

    void go() {
        println "Robot $name operating..."
        movements.each { movement -> println movement }
    }
}

class ForwardMove {
    def dist

    String toString() { "move distance... $dist" }
}

class LeftTurn {
    def rotation

    String toString() { "turn left... $rotation degrees" }
}

// 抽象工厂类
public abstract class AbstractFactory implements Factory {
    public boolean isLeaf() { return false; }
    public boolean onHandleNodeAttributes(FactoryBuilderSupport builder,
                                          Object node, Map attributes) { return true; }
    public void onNodeCompleted(FactoryBuilderSupport builder,
                                Object parent, Object node) { }
    public void setParent(FactoryBuilderSupport builder,
                          Object parent, Object child) { }
    public void setChild(FactoryBuilderSupport builder,
                         Object parent, Object child) { }
}

class RobotFactory extends AbstractFactory {
    def newInstance(FactoryBuilderSupport builder, name, value, Map attributes) {
        new Robot(name: value)
    }

    void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
        parent.movements << child
    }
}

class ForwardMoveFactory extends AbstractFactory {
    boolean isLeaf() { true }

    def newInstance(FactoryBuilderSupport builder, name, value, Map attributes) {
        new ForwardMove()
    }

    boolean onHandleNodeAttributes(FactoryBuilderSupport builder,
                                   Object node, Map attributes) {
        if (attributes.speed && attributes.duration) {
            node.dist = attributes.speed * attributes.duration
            attributes.remove('speed')
            attributes.remove('duration')
        }
        true
    }
}

class LeftTurnFactory extends AbstractFactory {
    boolean isLeaf() { true }

    def newInstance(FactoryBuilderSupport builder, name, value, Map attributes) {
        new LeftTurn()
    }
}

使用 RobotBuilder 创建机器人的示例代码如下:

def bldr = new RobotBuilder()
def robot = bldr.robot('iRobot') {
    forward(dist: 20)
    left(rotation: 90)
    forward(speed: 10, duration: 5)
}
robot.go()

上述代码通过 RobotBuilder 创建了一个机器人,并为其设置了移动和转向动作。当调用 robot.go() 方法时,机器人会按照设置的动作进行操作。

5. 总结

Groovy 的构建器为我们提供了一种 DSL 语法,用于执行诸如创建 XML 或 HTML 文档等繁琐任务。我们可以使用 Groovy 提供的现有构建器,也可以根据需要创建自定义构建器。不同类型的构建器适用于不同的场景,如 SwingBuilder 适用于创建 Swing 应用程序, BuilderSupport 适用于处理层次结构, FactoryBuilderSupport 适用于处理多个明确定义的节点。通过合理使用这些构建器,可以提高开发效率,使代码更加简洁和易于维护。

如果你创建了一个有用的构建器,不妨考虑将其贡献给社区,让更多的开发者受益。

通过以上介绍,相信你对 Groovy 构建器有了更深入的了解。希望这些知识能帮助你在实际开发中更好地运用 Groovy 构建器,提高开发效率和代码质量。

以下是一个简单的流程图,展示了使用不同构建器的决策过程:

graph TD;
    A[开始] --> B{是否有现成构建器?};
    B -- 是 --> C[使用现有构建器];
    B -- 否 --> D{任务类型};
    D -- 层次结构 --> E[使用BuilderSupport];
    D -- 多个明确定义节点 --> F[使用FactoryBuilderSupport];
    D -- 其他 --> G[使用元编程创建自定义构建器];
    C --> H[完成];
    E --> H;
    F --> H;
    G --> H;

同时,为了更清晰地对比不同构建器的特点,我们可以列出以下表格:
| 构建器类型 | 适用场景 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- |
| SwingBuilder | 创建 Swing 应用程序 | 代码简洁,结合闭包简化事件处理 | 仅适用于 Swing 场景 |
| BuilderSupport | 处理层次结构 | 提供节点结构识别方法,便于处理嵌套任务 | 处理不同类型节点较复杂 |
| FactoryBuilderSupport | 处理多个明确定义的节点 | 基于抽象工厂模式,代码清晰易维护 | 实现相对复杂 |
| 自定义构建器(元编程) | 特殊任务 | 灵活性高,可根据需求定制 | 开发成本较高 |

通过这个表格,你可以更直观地了解不同构建器的适用场景和优缺点,从而在实际开发中做出更合适的选择。

深入探索 Groovy 构建器:从 Swing 到自定义构建

6. 不同构建器的详细对比与应用场景分析

在前面的内容中,我们已经介绍了多种 Groovy 构建器,包括 SwingBuilder 、基于元编程的自定义构建器、 BuilderSupport FactoryBuilderSupport 。下面我们将进一步详细对比它们的特点,并分析在不同应用场景下的选择。

6.1 SwingBuilder
  • 特点 :专门用于创建 Swing 应用程序,通过简洁的语法和 Groovy 闭包,大大简化了 Swing 组件的创建和事件处理。
  • 应用场景 :当你需要快速开发一个 Swing 界面时, SwingBuilder 是首选。例如,开发一个简单的桌面应用程序,包含按钮、标签、文本框等组件,并且需要处理组件的事件。
  • 示例代码回顾
bldr = new groovy.swing.SwingBuilder()
frame = bldr.frame(
    title: 'Swing',
    size: [50, 100],
    layout: new java.awt.FlowLayout(),
    defaultCloseOperation: javax.swing.WindowConstants.EXIT_ON_CLOSE
) {
    lbl = label(text: 'test')
    btn = button(text: 'Click me', actionPerformed: {
        btn.text = 'Clicked'
        lbl.text = "Groovy!"
    })
}
frame.show()
6.2 基于元编程的自定义构建器
  • 特点 :利用 Groovy 的元编程能力,通过 methodMissing() propertyMissing() 方法处理未知的方法和属性调用,具有很高的灵活性,可以根据特定需求定制构建器。
  • 应用场景 :当没有现成的构建器满足需求,且任务具有一定的特殊性时,可以使用元编程创建自定义构建器。例如,创建一个待办事项列表构建器,处理复杂的任务嵌套和属性设置。
  • 示例代码回顾
class TodoBuilder {
    def level = 0
    def result = new StringWriter()

    def build(closure) {
        result << "To-Do:\n"
        closure.delegate = this
        closure()
        println result
    }

    def methodMissing(String name, args) {
        handle(name, args)
    }

    def propertyMissing(String name) {
        Object[] emptyArray = []
        handle(name, emptyArray)
    }

    def handle(String name, args) {
        level++
        level.times { result << " " }
        result << placeXifStatusDone(args)
        result << name.replaceAll("_", " ")
        result << printParameters(args)
        result << "\n"
        if (args.length > 0 && args[-1] instanceof Closure) {
            def theClosure = args[-1]
            theClosure.delegate = this
            theClosure()
        }
        level--
    }

    def placeXifStatusDone(args) {
        args.length > 0 && args[0] instanceof Map &&
                args[0]['status'] == 'done' ? "x " : "- "
    }

    def printParameters(args) {
        def values = ""
        if (args.length > 0 && args[0] instanceof Map) {
            values += " ["
            def count = 0
            args[0].each { key, value ->
                if (key == 'status') return
                count++
                values += (count > 1 ? " " : "")
                values += "${key}: ${value}"
            }
            values += "]"
        }
        values
    }
}
6.3 BuilderSupport
  • 特点 :提供了方便的方法来识别节点结构,适用于处理层次结构的任务。通过实现 setParent() createNode() 等方法,可以对节点的创建和处理进行控制。
  • 应用场景 :当任务具有明显的层次结构,如树形结构、嵌套任务等,可以使用 BuilderSupport 来处理。例如,创建一个文件目录结构的构建器,或者处理 XML 文档的层次结构。
  • 示例代码回顾
class TodoBuilderWithSupport extends BuilderSupport {
    int level = 0
    def result = new StringWriter()

    void setParent(parent, child) {}

    def createNode(name) {
        if (name == 'build') {
            result << "To-Do:\n"
            return 'buildnode'
        } else {
            return handle(name, [:])
        }
    }

    def createNode(name, Object value) {
        throw new Exception("Invalid format")
    }

    def createNode(name, Map attribute) {
        handle(name, attribute)
    }

    def createNode(name, Map attribute, Object value) {
        throw new Exception("Invalid format")
    }

    def propertyMissing(String name) {
        handle(name, [:])
        level--
    }

    void nodeCompleted(parent, node) {
        level--
        if (node == 'buildnode') {
            println result
        }
    }

    def handle(String name, attributes) {
        level++
        level.times { result << " " }
        result << placeXifStatusDone(attributes)
        result << name.replaceAll("_", " ")
        result << printParameters(attributes)
        result << "\n"
        name
    }

    def placeXifStatusDone(attributes) {
        attributes['status'] == 'done' ? "x " : "- "
    }

    def printParameters(attributes) {
        def values = ""
        if (attributes.size() > 0) {
            values += " ["
            def count = 0
            attributes.each { key, value ->
                if (key == 'status') return
                count++
                values += (count > 1 ? " " : "")
                values += "${key}: ${value}"
            }
            values += "]"
        }
        values
    }
}
6.4 FactoryBuilderSupport
  • 特点 :基于抽象工厂模式,根据节点名称将节点创建委托给不同的工厂,适用于处理多个明确定义的节点。可以通过注册不同的工厂来创建不同类型的节点,使代码更加清晰和易于维护。
  • 应用场景 :当任务涉及多个不同类型的节点,且每个节点的创建逻辑不同时,使用 FactoryBuilderSupport 可以避免 createNode() 方法的复杂性。例如,创建一个机器人编程的构建器,机器人的不同动作(如前进、左转等)可以由不同的工厂来创建。
  • 示例代码回顾
class RobotBuilder extends FactoryBuilderSupport {
    {
        registerFactory('robot', new RobotFactory())
        registerFactory('forward', new ForwardMoveFactory())
        registerFactory('left', new LeftTurnFactory())
    }
}

class Robot {
    String name
    def movements = []

    void go() {
        println "Robot $name operating..."
        movements.each { movement -> println movement }
    }
}

class ForwardMove {
    def dist

    String toString() { "move distance... $dist" }
}

class LeftTurn {
    def rotation

    String toString() { "turn left... $rotation degrees" }
}

// 抽象工厂类
public abstract class AbstractFactory implements Factory {
    public boolean isLeaf() { return false; }
    public boolean onHandleNodeAttributes(FactoryBuilderSupport builder,
                                          Object node, Map attributes) { return true; }
    public void onNodeCompleted(FactoryBuilderSupport builder,
                                Object parent, Object node) { }
    public void setParent(FactoryBuilderSupport builder,
                          Object parent, Object child) { }
    public void setChild(FactoryBuilderSupport builder,
                         Object parent, Object child) { }
}

class RobotFactory extends AbstractFactory {
    def newInstance(FactoryBuilderSupport builder, name, value, Map attributes) {
        new Robot(name: value)
    }

    void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
        parent.movements << child
    }
}

class ForwardMoveFactory extends AbstractFactory {
    boolean isLeaf() { true }

    def newInstance(FactoryBuilderSupport builder, name, value, Map attributes) {
        new ForwardMove()
    }

    boolean onHandleNodeAttributes(FactoryBuilderSupport builder,
                                   Object node, Map attributes) {
        if (attributes.speed && attributes.duration) {
            node.dist = attributes.speed * attributes.duration
            attributes.remove('speed')
            attributes.remove('duration')
        }
        true
    }
}

class LeftTurnFactory extends AbstractFactory {
    boolean isLeaf() { true }

    def newInstance(FactoryBuilderSupport builder, name, value, Map attributes) {
        new LeftTurn()
    }
}
7. 实际开发中的注意事项

在使用 Groovy 构建器进行实际开发时,有一些注意事项需要我们关注:

  • 性能考虑 :虽然构建器可以提高开发效率,但在某些情况下,可能会对性能产生一定的影响。例如,使用元编程创建自定义构建器时,由于方法和属性的动态处理,可能会增加一些额外的开销。因此,在对性能要求较高的场景中,需要进行性能测试和优化。
  • 错误处理 :在构建器的实现中,需要考虑错误处理。例如,在 createNode() 方法中,如果输入的参数不符合要求,应该抛出合适的异常,以便在开发过程中能够及时发现和解决问题。
  • 代码可读性 :虽然构建器可以使代码更加简洁,但在某些情况下,过于复杂的构建器实现可能会降低代码的可读性。因此,在编写构建器代码时,应该尽量保持代码的简洁和清晰,添加必要的注释,提高代码的可维护性。
8. 未来发展趋势

随着软件开发的不断发展,Groovy 构建器也可能会有一些新的发展趋势:

  • 与新技术的集成 :Groovy 构建器可能会与更多的新技术进行集成,如微服务、云计算等。例如,开发一个用于构建微服务架构的构建器,帮助开发者快速搭建微服务应用。
  • 更强大的元编程能力 :Groovy 的元编程能力可能会进一步增强,使得自定义构建器的开发更加灵活和高效。例如,支持更多的元编程特性,如动态代理、字节码操作等。
  • 社区贡献和开源生态 :随着越来越多的开发者使用 Groovy 构建器,社区贡献和开源生态可能会更加繁荣。开发者可以共享和交流自己的构建器实现,促进 Groovy 构建器的发展和应用。
9. 总结与展望

Groovy 的构建器为开发者提供了一种强大而灵活的工具,用于处理各种复杂的任务。通过使用不同类型的构建器,我们可以提高开发效率,使代码更加简洁和易于维护。在实际开发中,我们应该根据任务的特点和需求,选择合适的构建器,并注意性能、错误处理和代码可读性等方面的问题。

未来,随着软件开发技术的不断进步,Groovy 构建器有望在更多的领域得到应用,并不断发展和完善。希望开发者们能够充分利用 Groovy 构建器的优势,创造出更加优秀的软件产品。

以下是一个简单的列表,总结了使用 Groovy 构建器的步骤:
1. 确定任务需求,判断是否有现成的构建器可用。
2. 如果有现成构建器,直接使用;否则,根据任务类型选择合适的构建器创建方式(元编程、 BuilderSupport FactoryBuilderSupport )。
3. 实现构建器的具体逻辑,包括节点的创建、处理和属性设置。
4. 进行测试和调试,确保构建器的功能正常。
5. 在实际项目中使用构建器,并根据需要进行优化和扩展。

同时,为了更直观地展示不同构建器在不同场景下的使用频率,我们可以列出以下表格:
| 应用场景 | SwingBuilder | BuilderSupport | FactoryBuilderSupport | 自定义构建器(元编程) |
| ---- | ---- | ---- | ---- | ---- |
| 创建 Swing 应用程序 | 高 | 低 | 低 | 低 |
| 处理层次结构任务 | 低 | 高 | 低 | 中 |
| 处理多个明确定义节点 | 低 | 低 | 高 | 低 |
| 特殊任务 | 低 | 低 | 低 | 高 |

通过这个表格,我们可以更清晰地了解不同构建器在不同场景下的适用性,从而在实际开发中做出更明智的选择。

graph LR;
    A[确定任务需求] --> B{是否有现成构建器?};
    B -- 是 --> C[使用现有构建器];
    B -- 否 --> D{任务类型};
    D -- 层次结构 --> E[使用BuilderSupport];
    D -- 多个明确定义节点 --> F[使用FactoryBuilderSupport];
    D -- 其他 --> G[使用元编程创建自定义构建器];
    C --> H[实现构建器逻辑];
    E --> H;
    F --> H;
    G --> H;
    H --> I[测试和调试];
    I --> J[实际项目使用];
    J --> K[优化和扩展];

这个流程图展示了使用 Groovy 构建器的完整流程,从任务需求的确定到最终的优化和扩展,帮助开发者更好地理解和应用 Groovy 构建器。

基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样与统计,通过模拟系统元件的故障与修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构与设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码与案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高校研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行与可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理与实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估与优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更多不确定性因素进行深化研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值