使用Java语言编写设计模式-6.创建型模式-工厂模式

lingwh原创2022年9月8日大约 9 分钟约 2757 字

6.创建型模式-工厂模式

6.1.章节内容概述

本章节涉及主要内容有:
 6.1.章节内容概述
 6.2.章节内容大纲
 6.3.简介
 6.4.应用场景
 6.5.优缺点
 6.6.角色及其职责
 6.7.模型
 6.8.示例
 6.9.在开源框架中的应用
具体每个小节中包含的内容可使通过下面的章节内容大纲进行查看。

6.2.章节内容大纲

6.3.简介

工厂模式(Factory Pattern)又称工厂方法模式(FactoryMethod Pattern)是一种创建型设计模式,工厂方法模式是简单工厂模式的进一步抽象和推广,将原来在简单工厂中创建的对象改为在工厂类的子类中创建,是GoF设计模式的一种。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点,同时遵循OCP原则。在工厂方法模式中,提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类),并且由该实现类创建对应类的实例。这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。

6.4.应用场景

a.客户端不关心它所要创建对象的类(产品类)的时候
b.客户端知道它所要创建对象的类(产品类),但不关心如何创建的时候

6.5.优缺点

6.5.1.优点

a.使用工厂方法用来创建客户所需要的产品,隐藏了具体产品类的实例化过程,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名
b.加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。增加了系统的可扩展性,符合OCP开闭原则
c.每个产品都对应一个工厂,所以可以在这个产品对应的工厂中更为细致的控制产品的创建过程,而不会影响到其他的产品

6.5.2.缺点

a.每增加一个新产品,都需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,这样系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
b.由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,进而增加了系统的实现难度。

6.6.角色及其职责

AbstractProduct(抽象产品)
工厂类所创建的所有对象的父类,封装了产品对象的公共方法,所有的具体产品为其子类对象
ConcreteProduct(具体产品)
工厂类所创建的实际对象
AbstractFactory(抽象工厂)
提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类)
ConcreteFactory(具体工厂)
提供实际创建对象的方法

6.7.模型

6.7.1.模型类图

模型类图(有Client)
模型类图(无Client)

6.7.2.模型代码

Product.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

/**
 * Product(抽象产品)
 */
public abstract class AbstractProduct {
}

ConcreteProductA.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

/**
 * ConcreteProductA(具体产品A)
 */
public class ConcreteProductA extends AbstractProduct {
}

ConcreteProductB.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

/**
 * ConcreteProductB(具体产品B)
 */
public class ConcreteProductB extends AbstractProduct {
}

ConcreteProductC.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

/**
 * ConcreteProductC(具体产品C)
 */
public class ConcreteProductC extends AbstractProduct {
}

Factory.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

/**
 * AbstractFactory(抽象工厂)
 */
public abstract class AbstractFactory {
    /**
     * 通过工厂方法生产产品
     * @return
     */
    public abstract AbstractProduct factoryMethod();

    /**
     * 调用工厂方法的方法
     */
    public void operate(){
        AbstractProduct product = factoryMethod();
        System.out.println(product);
    }
}

ConcreteFactoryA.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

/**
 * ConcreteFactoryA(生产ProductA的具体工厂)
 */
public class ConcreteFactoryA extends AbstractFactory {
    @Override
    public AbstractProduct factoryMethod() {
        return new ConcreteProductA();
    }
}

ConcreteFactoryB.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

/**
 * ConcreteFactoryB(生产ProductB的具体工厂)
 */
public class ConcreteFactoryB extends AbstractFactory {
    @Override
    public AbstractProduct factoryMethod() {
        return new ConcreteProductB();
    }
}

ConcreteFactoryC.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

/**
 * ConcreteFactoryC(生产ProductC的具体工厂)
 */
public class ConcreteFactoryC extends AbstractFactory {
    @Override
    public AbstractProduct factoryMethod() {
        return new ConcreteProductC();
    }
}

Client.java
package com.dragonsoft.designpattern.create.factory.factorymethod.model;

import org.junit.Test;

/**
 * 客户端
 */
public class Client {
    @Test
    public void fun() {
        //获取生产产品A的工厂
        AbstractFactory factoryA = new ConcreteFactoryA();
        //生产产品A
        AbstractProduct productA = factoryA.factoryMethod();
        System.out.println(productA);
        //在工厂A中直接调用工厂方法
        factoryA.operate();

        System.out.println("--------------------");
        //获取生产产品B的工厂
        AbstractFactory factoryB = new ConcreteFactoryB();
        //生产产品B
        AbstractProduct productB = factoryB.factoryMethod();
        System.out.println(productB);
        //在工厂B中直接调用工厂方法
        factoryB.operate();

        System.out.println("--------------------");
        //获取生产产品C的工厂
        AbstractFactory factoryC = new ConcreteFactoryC();
        //生产产品C
        AbstractProduct productC = factoryC.factoryMethod();
        System.out.println(productC);
        //在工厂C中直接调用工厂方法
        factoryC.operate();
    }
}

6.8.示例

6.8.1.不使用工厂模式(实际上使用了简单工厂模式)

6.8.1.1.类图

类图(有Client)
类图(无Client)

6.8.1.1.代码

Pizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

import java.util.ArrayList;

public abstract class Pizza {
	protected String name;
	protected String dough;
	protected String sauce;
	protected ArrayList<String> toppings = new ArrayList<String>();

	public void prepare() {
		System.out.println("Prepare " + name);
		System.out.println("Tossing dough...");
		System.out.println("Adding sauce...");
		System.out.println("Adding toppings: ");
		for (String topping : toppings) {
			System.out.println("   " + topping);
		}
	}

	public void bake() {
		System.out.println("Bake for 25 minutes at 350");
	}

	public void cut() {
		System.out.println("Cut the pizza into diagonal slices");
	}

	public void box() {
		System.out.println("Place pizza in official PizzaStore box");
	}
 
	public String getName() {
		return name;
	}

	@Override
	public String toString() {
		StringBuffer display = new StringBuffer();
		display.append("---- " + name + " ----\n");
		display.append(dough + "\n");
		display.append(sauce + "\n");
		for (String topping : toppings) {
			display.append(topping + "\n");
		}
		return display.toString();
	}
}

ChicagoStyleCheesePizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

public class ChicagoStyleCheesePizza extends Pizza {

	public ChicagoStyleCheesePizza() { 
		name = "Chicago Style Deep Dish Cheese Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
	}
 
	@Override
	public void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

ChicagoStyleClamPizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

public class ChicagoStyleClamPizza extends Pizza {
	public ChicagoStyleClamPizza() {
		name = "Chicago Style Clam Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
		toppings.add("Frozen Clams from Chesapeake Bay");
	}
 
	@Override
	public void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

ChicagoStylePepperoniPizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

public class ChicagoStylePepperoniPizza extends Pizza {
	public ChicagoStylePepperoniPizza() {
		name = "Chicago Style Pepperoni Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
		toppings.add("Black Olives");
		toppings.add("Spinach");
		toppings.add("Eggplant");
		toppings.add("Sliced Pepperoni");
	}
 
	@Override
	public void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

ChicagoStyleVeggiePizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

public class ChicagoStyleVeggiePizza extends Pizza {
	public ChicagoStyleVeggiePizza() {
		name = "Chicago Deep Dish Veggie Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
		toppings.add("Black Olives");
		toppings.add("Spinach");
		toppings.add("Eggplant");
	}
 
	@Override
	public void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

NYStyleCheesePizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

public class NYStyleCheesePizza extends Pizza {

	public NYStyleCheesePizza() { 
		name = "NY Style Sauce and Cheese Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
	}
}


NYStyleClamPizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

public class NYStyleClamPizza extends Pizza {
	
	public NYStyleClamPizza() {
		name = "NY Style Clam Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
		toppings.add("Fresh Clams from Long Island Sound");
	}
}

NYStylePepperoniPizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

public class NYStylePepperoniPizza extends Pizza {

	public NYStylePepperoniPizza() {
		name = "NY Style Pepperoni Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
		toppings.add("Sliced Pepperoni");
		toppings.add("Garlic");
		toppings.add("Onion");
		toppings.add("Mushrooms");
		toppings.add("Red Pepper");
	}
}

NYStyleVeggiePizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

public class NYStyleVeggiePizza extends Pizza {

	public NYStyleVeggiePizza() {
		name = "NY Style Veggie Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
		toppings.add("Garlic");
		toppings.add("Onion");
		toppings.add("Mushrooms");
		toppings.add("Red Pepper");
	}
}

PizzaStore.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

/**
 * 不使用工厂(方法)模式(实际上使用了简单工厂模式)
 */
public class PizzaStore {
	public Pizza createPizza(String pizzaStyle, String pizzaType) {
		Pizza pizza = null;
		if (pizzaStyle.equals("NY")) {
			if (pizzaType.equals("cheese")) {
				pizza = new NYStyleCheesePizza();
			} else if (pizzaType.equals("veggie")) {
				pizza = new NYStyleVeggiePizza();
			} else if (pizzaType.equals("clam")) {
				pizza = new NYStyleClamPizza();
			} else if (pizzaType.equals("pepperoni")) {
				pizza = new NYStylePepperoniPizza();
			}
		} else if (pizzaStyle.equals("Chicago")) {
			if (pizzaType.equals("cheese")) {
				pizza = new ChicagoStyleCheesePizza();
			} else if (pizzaType.equals("veggie")) {
				pizza = new ChicagoStyleVeggiePizza();
			} else if (pizzaType.equals("clam")) {
				pizza = new ChicagoStyleClamPizza();
			} else if (pizzaType.equals("pepperoni")) {
				pizza = new ChicagoStylePepperoniPizza();
			}
		} else {
			System.out.println("Error: invalid type of pizza");
			return null;
		}
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}

	/**
	 * 调用工厂方法的方法
	 * @param pizzaType
	 * @return
	 */
	public Pizza orderPizza(String pizzaStyle, String pizzaType) {
		Pizza pizza = createPizza(pizzaStyle,pizzaType);
		System.out.println("--- Making a " + pizza.getName() + " ---");
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}

Client.java
package com.dragonsoft.designpattern.create.factory.factorymethod.nouse;

import org.junit.Test;

public class Client {
	@Test
	public void fun() {
		//测试DependentPizzaFactory
		PizzaStore pizzaStore = new PizzaStore();
		Pizza pizza = pizzaStore.orderPizza("NY", "cheese");
		System.out.println("Lee ordered a " + pizza.getName() + "\n");
	}
}

6.8.2.使用工厂模式

6.8.2.1.类图

类图(有Client)
类图(无Client)

6.8.2.2.代码

Pizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

import java.util.ArrayList;

public abstract class Pizza {
	protected String name;
	protected String dough;
	protected String sauce;
	protected ArrayList<String> toppings = new ArrayList<String>();

	public void prepare() {
		System.out.println("Prepare " + name);
		System.out.println("Tossing dough...");
		System.out.println("Adding sauce...");
		System.out.println("Adding toppings: ");
		for (String topping : toppings) {
			System.out.println("   " + topping);
		}
	}

	public void bake() {
		System.out.println("Bake for 25 minutes at 350");
	}

	public void cut() {
		System.out.println("Cut the pizza into diagonal slices");
	}

	public void box() {
		System.out.println("Place pizza in official PizzaStore box");
	}
 
	public String getName() {
		return name;
	}

	@Override
	public String toString() {
		StringBuffer display = new StringBuffer();
		display.append("---- " + name + " ----\n");
		display.append(dough + "\n");
		display.append(sauce + "\n");
		for (String topping : toppings) {
			display.append(topping + "\n");
		}
		return display.toString();
	}
}

ChicagoStyleCheesePizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class ChicagoStyleCheesePizza extends Pizza {

	public ChicagoStyleCheesePizza() { 
		name = "Chicago Style Deep Dish Cheese Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
	}
 
	@Override
	public void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

ChicagoStyleClamPizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class ChicagoStyleClamPizza extends Pizza {
	public ChicagoStyleClamPizza() {
		name = "Chicago Style Clam Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
		toppings.add("Frozen Clams from Chesapeake Bay");
	}
 
	@Override
	public void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

ChicagoStylePepperoniPizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class ChicagoStylePepperoniPizza extends Pizza {
	public ChicagoStylePepperoniPizza() {
		name = "Chicago Style Pepperoni Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
		toppings.add("Black Olives");
		toppings.add("Spinach");
		toppings.add("Eggplant");
		toppings.add("Sliced Pepperoni");
	}
 
	@Override
	public void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

ChicagoStyleVeggiePizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class ChicagoStyleVeggiePizza extends Pizza {
	public ChicagoStyleVeggiePizza() {
		name = "Chicago Deep Dish Veggie Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
		toppings.add("Black Olives");
		toppings.add("Spinach");
		toppings.add("Eggplant");
	}
 
	@Override
	public void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

NYStyleCheesePizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class NYStyleCheesePizza extends Pizza {

	public NYStyleCheesePizza() { 
		name = "NY Style Sauce and Cheese Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
	}
}


NYStyleClamPizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class NYStyleClamPizza extends Pizza{
	
	public NYStyleClamPizza() {
		name = "NY Style Clam Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
		toppings.add("Fresh Clams from Long Island Sound");
	}
}

NYStylePepperoniPizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class NYStylePepperoniPizza extends Pizza {

	public NYStylePepperoniPizza() {
		name = "NY Style Pepperoni Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
		toppings.add("Sliced Pepperoni");
		toppings.add("Garlic");
		toppings.add("Onion");
		toppings.add("Mushrooms");
		toppings.add("Red Pepper");
	}
}

NYStyleVeggiePizza.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class NYStyleVeggiePizza extends Pizza {

	public NYStyleVeggiePizza() {
		name = "NY Style Veggie Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
		toppings.add("Garlic");
		toppings.add("Onion");
		toppings.add("Mushrooms");
		toppings.add("Red Pepper");
	}
}

PizzaFactory.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public abstract class PizzaStoreFactory {
	
	/**
	 * 工厂方法
	 * @param pizzaType
	 * @return
	 */
	public abstract Pizza createPizza(String pizzaType);

	/**
	 * 调用工厂方法的方法
	 * @param pizzaType
	 * @return
	 */
	public Pizza orderPizza(String pizzaType) {
		Pizza pizza = createPizza(pizzaType);
		System.out.println("--- Making a " + pizza.getName() + " ---");
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}

NYPizzaFactory.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class NYPizzaStoreFactory extends PizzaStoreFactory {

	@Override
	public Pizza createPizza(String pizzaType) {
		if (pizzaType.equals("cheese")) {
			return new NYStyleCheesePizza();
		} else if (pizzaType.equals("veggie")) {
			return new NYStyleVeggiePizza();
		} else if (pizzaType.equals("clam")) {
			return new NYStyleClamPizza();
		} else if (pizzaType.equals("pepperoni")) {
			return new NYStylePepperoniPizza();
		} else {
			return null;
		}
	}
}

ChicagoPizzaStoreFactory.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

public class ChicagoPizzaStoreFactory extends PizzaStoreFactory {

	@Override
	public Pizza createPizza(String pizzaType) {
    	if (pizzaType.equals("cheese")) {
        		return new ChicagoStyleCheesePizza();
    	} else if (pizzaType.equals("veggie")) {
    	    	return new ChicagoStyleVeggiePizza();
    	} else if (pizzaType.equals("clam")) {
    	    	return new ChicagoStyleClamPizza();
    	} else if (pizzaType.equals("pepperoni")) {
        		return new ChicagoStylePepperoniPizza();
    	} else {
			return null;
		}
	}
}

Client.java
package com.dragonsoft.designpattern.create.factory.factorymethod.use;

import org.junit.Test;

public class Client {
	@Test
	public void fun() {
		PizzaStoreFactory nyPizzaStoreFactory = new NYPizzaStoreFactory();
		PizzaStoreFactory chicagoPizzaStoreFactory = new ChicagoPizzaStoreFactory();
 
		Pizza pizza = nyPizzaStoreFactory.orderPizza("cheese");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoPizzaStoreFactory.orderPizza("cheese");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");

		pizza = nyPizzaStoreFactory.orderPizza("clam");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoPizzaStoreFactory.orderPizza("clam");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");

		pizza = nyPizzaStoreFactory.orderPizza("pepperoni");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoPizzaStoreFactory.orderPizza("pepperoni");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");

		pizza = nyPizzaStoreFactory.orderPizza("veggie");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoPizzaStoreFactory.orderPizza("veggie");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");
	}
}

6.9.在开源框架中的应用

6.9.1.在JDK中的应用

JDK8#集合框架部分代码
说明
Collection是抽象工厂,Collection中iterator()方法就是工厂方法,ArrayList和HashSet就是具体工厂,Iterator是抽象产品,具体产品是ArrayList和HasHSet中iterator()方法的return new Itr()创建出来的对象

类图
代码
    Iterable.java
public interface Iterable<T> {
    Iterator<T> iterator();
}
    Collection.java
public interface Collection<E> extends Iterable<E> {
    Iterator<E> iterator();
}
    AbstractCollection.java
public abstract class AbstractCollection<E> implements Collection<E> {
    public abstract Iterator<E> iterator();
}
    AbstractList.java
public abstract class AbstractList<E> extends AbstractCollection<E> {
    public Iterator<E> iterator() {
        return new Itr();
    }
}
    ArrayList.java
public class ArrayList<E> extends AbstractList<E> {
    public Iterator<E> iterator() {
        return new Itr();
    }
}
    AbstractSet.java
public abstract class AbstractSet<E> extends AbstractCollection<E> {
    public abstract Iterator<E> iterator();
}
    HashSet.java
public class HashSet<E> extends AbstractSet<E> implements Set<E> {
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
}
上次编辑于: 2022/9/10 01:42:04
贡献者: lingwh
评论