《设计模式》设计模式的基本原则
《设计模式》设计模式的基本原则
《设计模式》单例模式
《设计模式》工厂模式
《设计模式》原型模式
《设计模式》建造者模式
《设计模式》适配器模式
《设计模式》桥接模式
《设计模式》装饰者模式
《设计模式》组合模式
《设计模式》外观模式
《设计模式》享元模式
《设计模式》代理模式
《设计模式》模板方法模式
《设计模式》命令模式
《设计模式》设计模式的基本原则
- 1. 单一职责原则
- 2. 接口隔离原则
- 3. 依赖倒转原则
- 4. 里氏替换原则
- 5. 开闭原则
- 6. 迪米特法则
设计模式的基本原则就是设计模式设计的依据所在,是设计模式的基础。开发人员在编码时需要遵守这些基本原则,才能使得编写的代码可维护、可扩展、可重用、灵活性强,主要有六个基本原则:单一职责原则、接口隔离原则、依赖倒转原则、里氏替换原则、开闭原则和迪米特法则。
1. 单一职责原则
定义:一个类只负责一项职责,如类 A 负责两个不同职责:职责1 和职责 2. 当职责1 需求变更而改变类 A 时,可能造成职责2 执行错误,因此需要将类 A 的粒度分解为:A1, A2.
应用案例:
1、有一个交通工具类 Vehicle, 其包含实例方法 run()
,不同的交通工具(摩托车、汽车、飞机等)通过调用 run()
方法“启动”。
public class singleresponsibility {public static void main(String[] args) {Vehicle vehicle = new Vehicle();vehicle.run("摩托车");vehicle.run("汽车");vehicle.run("飞机");vehicle.run("轮船");}
}class Vehicle {public void run(String vehicle) {System.out.println(vehicle+"在公路上运行...");}
}
在案例1中的 run()
方法,类 Vehicle 负责了多项职责,即摩托车、汽车和飞机的运行,这就违反了单一职责原则。
2、为了遵守单一职责原则,应该让 Vehicle 类只负责一项职责,可以将 Vehicle 类分解为多个不同的类,根据不同的交通工具调用不同类的 run()
方法。
public class singleresponsibility {public static void main(String[] args) {RoadVehicle roadVehicle = new RoadVehicle();roadVehicle.run("摩托车");roadVehicle.run("汽车");AirVehicle airVehicle = new AirVehicle();airVehicle.run("飞机");WaterVehicle waterVehicle = new WaterVehicle();waterVehicle.run("轮船");}
}class RoadVehicle {public void run(String vehicle) {System.out.println(vehicle+"公路运行");}
}
class AirVehicle {public void run(String vehicle) {System.out.println(vehicle+"天空运行");}
}
class WaterVehicle {public void run(String vehicle) {System.out.println(vehicle+"水中运行");}
}
案例2中将类进行了分解,使得每个类只负责一项职责,遵守了单一职责原则。但是,可以发现将类进行分解之后,不仅需要增加新的类,而且还要改动客户端调用的代码。
3、为了减少代码的改动,并且还要遵守单一职责原则,可以选择在类中增加方法,达到这样的目的。
public class singleresponsibility {public static void main(String[] args) {Vehicle vehicle = new Vehicle();vehicle.run("摩托车");vehicle.run("汽车");vehicle.runAir("飞机");vehicle.runWater("轮船");}
}class Vehicle {public void run(String vehicle) {System.out.println(vehicle+"在公路上运行...");}public void runAir(String vehicle) {System.out.println(vehicle+"在天空上运行...");}public void runWater(String vehicle) {System.out.println(vehicle+"在水中上运行...");}
}
单一职责原则注意事项:
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性和可维护性。
- 降低变更引起的风险。
- 通常情况下,我们应当遵守单一职责原则,但是当逻辑非常简单时,可以在代码级别违反单一职责原则。只有类中方法数量足够少时,才可以在方法级别遵守单一职责原则。
2. 接口隔离原则
定义:一个类对另一个类的依赖应该建立在最小的接口,客户端不应该依赖它不需要的接口。
应用案例:
1、类 A 通过接口 Interface1 依赖类 B,但是只会使用接口的1,2,3方法,类 C 通过接口 Interface1 依赖类 D,但只会使用接口的1,4,5方法,其 UML 图和实现代码如下所示:
interface Interface1 {void operation1();void operation2();void operation3();void operation4();void operation5();
}
class B implements Interface1 {@Overridepublic void operation1() {System.out.println("B 实现了 operation1");}@Overridepublic void operation2() {System.out.println("B 实现了 operation2");}@Overridepublic void operation3() {System.out.println("B 实现了 operation3");}@Overridepublic void operation4() {System.out.println("B 实现了 operation4");}@Overridepublic void operation5() {System.out.println("B 实现了 operation5");}
}
class D implements Interface1 {@Overridepublic void operation1() {System.out.println("D 实现了 operation1");}@Overridepublic void operation2() {System.out.println("D 实现了 operation2");}@Overridepublic void operation3() {System.out.println("D 实现了 operation3");}@Overridepublic void operation4() {System.out.println("D 实现了 operation4");}@Overridepublic void operation5() {System.out.println("D 实现了 operation5");}
}
class A { //A类通过接口Interface1依赖(使用)B类,但是只会使用到operation1,2,3public void depend1(Interface1 i) {i.operation1();}public void depend2(Interface1 i) {i.operation2();}public void depend3(Interface1 i) {i.operation3();}
}
class C { //C类通过接口Interface1依赖(使用)D类,但是只会用到1,4,5方法public void depend1(Interface1 i) {i.operation1();}public void depend4(Interface1 i) {i.operation4();}public void depend5(Interface1 i) {i.operation5();}
}
在案例1中,类 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,而接口 Interface1 对于类 A 和类 C 来说不是最小接口,那么类 B 和类 D 必须实现它们不需要的方法,这就违反了接口隔离原则。
2、为了让类 B 和类 D 不用实现它们不需要的方法,可以将接口 Interface1 拆分为三个接口,类 A 和类 C 分别与他们需要的接口建立依赖关系,其 UML 图和实现代码如下所示:
public class Segregation1 {public static void main(String[] args) {A a = new A();a.depend1(new B()); //A类通过接口去依赖(使用)B类a.depend2(new B());a.depend3(new B());C c = new C();c.depend1(new D()); //C类通过接口去依赖(使用)D类c.depend4(new D());c.depend5(new D());}
}
//接口
interface Interface1 {void operation1();
}
interface Interface2 {void operation2();void operation3();
}
interface Interface3 {void operation4();void operation5();
}
class B implements Interface1, Interface2 {@Overridepublic void operation1() {System.out.println("B 实现了 operation1");}@Overridepublic void operation2() {System.out.println("B 实现了 operation2");}@Overridepublic void operation3() {System.out.println("B 实现了 operation3");}
}
class D implements Interface1, Interface3 {@Overridepublic void operation1() {System.out.println("D 实现了 operation1");}@Overridepublic void operation4() {System.out.println("D 实现了 operation4");}@Overridepublic void operation5() {System.out.println("D 实现了 operation5");}
}
class A { //A类通过接口Interface1依赖(使用)B类,但是只会使用到operation1,2,3public void depend1(Interface1 i) {i.operation1();}public void depend2(Interface2 i) {i.operation2();}public void depend3(Interface2 i) {i.operation3();}
}
class C { //C类通过接口Interface1依赖(使用)D类,但是只会用到1,4,5方法public void depend1(Interface1 i) {i.operation1();}public void depend4(Interface3 i) {i.operation4();}public void depend5(Interface3 i) {i.operation5();}
}
3. 依赖倒转原则
定义:
- 高层模块不应该依赖底层模块,两者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
- 依赖倒转的核心思想是面向接口编程:相对于细节的多变性,抽象要稳定的多,以抽象为基础搭建的架构比以细节为基础搭建的架构稳定的多。在面向对象编程语言中,这里的抽象就是指接口或者抽象类,细节就是指具体的实现类。
- 使用接口或抽象类是制定好规范,不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
应用案例:
1、有一个 Person 类,通过调用它的实例方法 receive()
来接收信息。
public class DependencyInversion {public static void main(String[] args) {Person person = new Person();person.receive(new Email());}
}
class Email {public String getInfo() {return "电子邮件信息:hello,world";}
}
class Person {public void receive(Email email) {System.out.println(email.getInfo());}
}
在案例1中,如果获取的对象是微信,短信等,则新增类 Wechat 同时 Person 也要增加相应的接收方法,这就使得高层模块依赖了底层模块,而不是依赖其抽象。
2、引入一个抽象的接口 IReceiver
表示接收者,Eemail
和 Wechat
各自实现接口 IReceiver
,之后 Person
类与接口 IReceiver
发生依赖,符合依赖倒转原则。
public class DependencyInversion {public static void main(String[] args) {//客户端无需改变Person person = new Person();person.receive(new Email());person.receive(new Wechat());}
}
interface IReceiver {String getInfo();
}
class Email implements IReceiver{@Overridepublic String getInfo() {return "电子邮件信息:hello,world";}
}
class Wechat implements IReceiver {@Overridepublic String getInfo() {return "微信消息:hello,world";}
}
class Person {public void receive(IReceiver receiver) {System.out.println(receiver.getInfo());}
}
依赖关系传递的三种方式:
1、接口传递
public class DependencyPass {public static void main(String[] args) {OpenAndClose openAndClose = new OpenAndClose();openAndClose.open(new HUAWEI());}
}
interface IOpenAndClose {public void open(ITV tv);
}
interface ITV {public void play();
}
class HUAWEI implements ITV {@Overridepublic void play() {System.out.println("打开华为电视机");}
}
class OpenAndClose implements IOpenAndClose {@Overridepublic void open(ITV tv) {tv.play();}
}
2、构造器传递
public class DependencyPass {public static void main(String[] args) {OpenAndClose openAndClose = new OpenAndClose(new HUAWEI());openAndClose.open();}
}
interface IOpenAndClose {void open();
}
interface ITV {void play();
}
class HUAWEI implements ITV {@Overridepublic void play() {System.out.println("打开华为电视机");}
}
class OpenAndClose implements IOpenAndClose {public ITV tv;public OpenAndClose(ITV tv) {this.tv = tv;}@Overridepublic void open() {tv.play();}
}
3、通过 setter 方式传递
public class DependencyPass {public static void main(String[] args) {OpenAndClose openAndClose = new OpenAndClose();openAndClose.setTv(new HUAWEI());openAndClose.open();}
}
interface IOpenAndClose {void open();void setTv(ITV tv);
}
interface ITV {void play();
}
class HUAWEI implements ITV {@Overridepublic void play() {System.out.println("打开华为电视机");}
}
class OpenAndClose implements IOpenAndClose {private ITV tv;@Overridepublic void open() {tv.play();}@Overridepublic void setTv(ITV tv) {this.tv = tv;}
}
依赖倒转原则的注意事项:
- 低层模块尽量都要有抽象类或接口,或两者都有,这样程序的稳定性才更好。
- 变量的声明类型尽量是抽象类或接口,这样变量的引用和实际对象间存在一个缓冲,有利于程序扩展和优化。
4. 里氏替换原则
定义:如果对每个类型为 T1 的对象 o1, 都有类型为 T2 的对象 o2, 使得以 T1 定义的所有程序 P 在所有的对象 o1 都替换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。换而言之,所有引用基类的地方必须能够透明地使用其子类的对象。
应用案例:
1、类 A 作为基类,类 B 继承类 A 并重写了类 A 中的 func()
方法。
public class Liskov {public static void main(String[] args) {A a = new A();System.out.println("11-3="+a.func(11, 3));System.out.println("--------------------");B b = new B();System.out.println("11-3="+b.func(11, 3));//本意是求出11-3,却变成11+3}
}
//A类
class A {//增加新的功能,这里重写了A类的方法,可能是无意识的public int func(int num1, int num2) {return num1 - num2;}
}
class B extends A {//增加新的功能,无意识地重写了A类的方法public int func(int a, int b) {return a + b;}
}
- 在案例1中,类 B 重写了基类 A 的
func1()
方法,即使可能是无意识的重写,原本行为是实现两数相加却变成了两数相减,改变了程序的行为这就违背了里氏替换原则。 - 在实际场景中,通过重写父类的方法完成新的功能这种方式实现起来确实简单,但是将会导致整个继承体系的复用性会比较差,特别是在多态使用频繁的时候。
2、为了遵守里氏替换原则,可以将原有的继承关系去掉,让原来的父子类都继承一个基类,采用依赖、聚合和组合关系替代继承关系,其 UML 图和实现代码如下所示:
public class Liskov {public static void main(String[] args) {A a = new A();System.out.println("11-3="+a.func1(11, 3));System.out.println("--------------------");B b = new B();System.out.println("11+3="+b.func1(11, 3));//这里本意是求出11+3System.out.println("11-3="+b.func2(11, 3)); //11-3}
}
//创建一个更加基础的基类
class Base {//把更加基础的方法和成员写到Base类
}
class A extends Base{public int func1(int num1, int num2) {//返回两个数的差return num1 - num2;}
}
class B extends Base {//如果B类需要使用A类的方法,使用组合关系private A a = new A();public int func1(int a, int b) {return a + b;}//仍然想使用A的方法public int func2(int a, int b) {return a.func1(a,b);}
}
里氏替换原则的注意事项:
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
- 继承让两个类的耦合性变得更强,为了降低类间的耦合性,适当情况下可以使用聚合、组合和依赖来解决问题。
5. 开闭原则
定义:
- 一个软件实体类,模块和函数应该对扩展对扩展开放(对服务提供方开放),对修改关闭(对服务使用方),用抽象构建框架用实体扩展细节。
- 当软件需求发生变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码实现变化。
应用案例:
1、有一个画图类 Graphical
, 通过调用方法 drawShape(Shape s)
进行画图,根据传入的 s.shape
可以画出不同的图形,其 UML 图和实现代码如下所示:
public class OCP {public static void main(String[] args) {GraphicEditor graphicEditor = new GraphicEditor();graphicEditor.drawShape(new Rectangle());graphicEditor.drawShape(new Circle());}
}
class GraphicEditor {public void drawShape(Shape s) {if (s.m_type == 1) {drawRectangle(s);} else if (s.m_type == 2) {drawCircle(s);}}public void drawRectangle(Shape r) {System.out.println("绘制矩形");}public void drawCircle(Shape r) {System.out.println("绘制圆形");}
}class Shape {int m_type;
}class Rectangle extends Shape {Rectangle() {super.m_type = 1;}
}class Circle extends Shape {Circle() {super.m_type = 2;}
}
在案例1中,如果需要新增一个可以画三角形的功能,不仅需要新增类 Triangle
继承 Shape
,与此同时还要修改类 GraphicEditor
,在 drawShape(Shape s)
新加一个分支,并且还要增加一个 drawTriangle(Shape s)
方法。这一系列行为都是对使用方的修改,违背了开闭原则。
2、为了遵守开闭原则,可以考虑将 Shape
作为抽象类并提供抽象方法 draw()
,让子类实现抽象类 Shape
,每当有新的图形种类增加时,可以直接继承 Shape
并实现 draw()
方法,无须对使用方进行修改。
public class OCP {public static void main(String[] args) {GraphicEditor graphicEditor = new GraphicEditor();graphicEditor.drawShape(new Rectangle());graphicEditor.drawShape(new Circle());graphicEditor.drawShape(new Triangle());graphicEditor.drawShape(new OtherGraphic());}
}class GraphicEditor {public void drawShape(Shape s) {s.draw();}
}
abstract class Shape {public abstract void draw();
}class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("绘制矩形");}
}class Circle extends Shape {@Overridepublic void draw() {System.out.println("绘制圆形");}
}class Triangle extends Shape {@Overridepublic void draw() {System.out.println("绘制三角形");}
}
class OtherGraphic extends Shape {@Overridepublic void draw() {System.out.println("绘制其他图形");}
}
6. 迪米特法则
定义:
- 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。不管被依赖的类多么复杂,都尽量将逻辑封装在类的内部,对外除了
public
方法不泄露任何信息。 - 迪米特法则还有个更简单的定义:只与直接的朋友通信。
- 每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式有很多:依赖、关联、组合和聚合等。
- 其中,出现在成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类不是朋友关系,陌生的类最好不要以局部变量的形式出现在类的内部。
应用案例:
1、有一个学校,下面有总部和各个学院,要求打印出学校总部员工和各个学院员工的 ID.
public class Demeter {public static void main(String[] args) {//创建了一个SchoolManager对象SchoolManager schoolManager = new SchoolManager();//输出学院员工和学校总部员工IDschoolManager.printAllEmployee(new CollegeManager());}
}
//学校总部员工
class Employee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
}
class CollegeEmployee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
}
class CollegeManager {//返回学院所有员工public List<CollegeEmployee> getAllEmployee() {ArrayList<CollegeEmployee> list = new ArrayList<>();for (int i = 0; i < 10; i++) {CollegeEmployee emp = new CollegeEmployee();emp.setId("学院员工id="+i);list.add(emp);}return list;}
}class SchoolManager {//返回学校总部所有员工public List<Employee> getAllEmployee() {ArrayList<Employee> list = new ArrayList<>();for (int i = 0; i < 5; i++) {Employee emp = new Employee();emp.setId("学校总部员工id="+i);list.add(emp);}return list;}//输出学校总部和学院员工信息public void printAllEmployee(CollegeManager sub) { //CollegeManager作为printAllEmployee局部变量List<CollegeEmployee> list1 = sub.getAllEmployee();System.out.println("--------------分公司员工-------------");for (CollegeEmployee e : list1) {System.out.println(e.getId());}List<Employee> list2 = this.getAllEmployee();System.out.println("-------------学校总部员工-------------");for (Employee e : list2) {System.out.println(e.getId());}}
}
在案例1中,CollegeEmployee
以局部变量的形式出现在 SchoolManager
类中,作为 SchoolManager
的陌生类,并不是它的直接朋友,这就违背了迪米特法则。
2、为了遵守迪米特法则,在类中应该避免这种非直接朋友关系的耦合,将 printEmployee()
方法封装到类 CollegeManager
中,而将类 CollegeManager
作为类 SchoolManager
的直接朋友。
public class Demeter1 {public static void main(String[] args) {System.out.println("使用迪米特法则的改进");//创建了一个SchoolManager对象SchoolManager schoolManager = new SchoolManager();//输出学院员工id 和 学校总部员工信息schoolManager.printAllEmployee(new CollegeManager());}
}//学校总部员工
class Employee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
}class CollegeEmployee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
}class CollegeManager {//返回学院所有员工public List<CollegeEmployee> getAllEmployee() {ArrayList<CollegeEmployee> list = new ArrayList<>();for (int i = 0; i < 10; i++) {CollegeEmployee emp = new CollegeEmployee();emp.setId("学院员工id="+i);list.add(emp);}return list;}public void printEmployee() {List<CollegeEmployee> list1 = this.getAllEmployee();System.out.println("--------------分公司员工-------------");for (CollegeEmployee e : list1) {System.out.println(e.getId());}}
}class SchoolManager {//返回学校总部所有员工public List<Employee> getAllEmployee() {ArrayList<Employee> list = new ArrayList<>();for (int i = 0; i < 5; i++) {Employee emp = new Employee();emp.setId("学校总部员工id="+i);list.add(emp);}return list;}//输出学校总部和学院员工信息public void printAllEmployee(CollegeManager sub) {//将获取学院员工的方法封装到CollegeManager类里面sub.printEmployee();List<Employee> list2 = this.getAllEmployee();System.out.println("-------------学校总部员工-------------");for (Employee e : list2) {System.out.println(e.getId());}}
}
迪米特法则的注意事项:
- 核心思想是降低类之间的耦合。
- 对每个类减少不必要的依赖,降低类之间的耦合关系,并不是严格要求完全没有依赖关系。
《设计模式》设计模式的基本原则相关推荐
- 图解设计模式-设计模式七大原则
Java设计模式 设计模式七大原则 设计模式的目的 编写软件过程中,程序员面临来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让 **程序(软件)**具有更好的 ...
- 设计模式 ----- 设计模式总结
设计模式 -– 设计模式总结 设计模式系列源码: https://github.com/liloqian/DesiginModeDemo 1.什么是设计模式 模式是指在某情形下,针对某问题的某种解决方 ...
- Java设计模式---设计模式概述及七大原则
网课指路:尚硅谷Java设计模式(图解+框架源码剖析)_哔哩哔哩_bilibili 设计模式介绍 1) 设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验, 模式不是代码 ...
- 正确的姿势学习设计模式,设计模式必知必会 --- 面试, 提升篇
目录 引言--- 为何需要设计模式? 学习设计模式不如掌握设计原则 (根据原则慢慢重构自己的设计模式) 依赖倒置原则 开放封闭原则 面向接口编程 封装变化点(核心原则) 抽象稳定点,扩展变化点 单一职 ...
- JavaScript 设计模式----设计模式初识
JavaScript 设计模式----设计模式初识 1. 从设计到模式 2. 设计模式简介 2.1 设计模式类型 2.2 创建型 2.3 结构型 2.4 行为型 3. 设计原则面试题 3.1 面试题一 ...
- 面向对象设计模式5大基本原则
"宇宙万物之中,没有一样东西能像思想那么顽固." 一爱默生 首先明确模式是针对面向对象的,它的三大特性,封装.继承.多态. 面向对象设计模式有5大基本原则:单一职责原 ...
- 设计模式之七大基本原则
做什么事都需要遵循一些准则,设计模式也不例外.我们在设计一些设计模式时,一般遵循如下七项基本原则,它们分别是: 单一职责原则 (Single Responsibility Principle) 开放- ...
- 设计模式6大基本原则
六大基本原则 总原则 原则 总原则 开放-封闭原则:对修改进行封闭,在原有程序需要拓展的时候,不能去修改原有的代码逻辑,而是进行拓展,实现一个热插拔(可以新增或者舍弃),这样可以使程序的扩展性更好,易 ...
- 设计模式---设计模式的分类及六大原则
设计模式 1.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种: 工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式. 结构性模型,共七种: 适配器模型,装饰器模型,代 ...
- [设计模式]设计模式SOLID原则
在程序设计领域, SOLID(单一功能.开闭原则.里氏替换.接口隔离以及依赖倒置)是由罗伯特·C·马丁在21世纪早期引入的记忆术首字母缩略字,指代了面向对象编程和面向对象设计的五个基本原则.当这些原则 ...
最新文章
- Linux环境kafka安装
- ext form验证tip_FormValidator表单验证
- “load”: 不是“torch::jit”的成员
- 实验——LINUX常用网络测试命令
- 九阴真经战无不胜服务器位置,九阴真经各门派武功风水宝地分类及坐标大全
- Macbook使用技巧:如何在外部显示器上获得4K 60 FPS
- yml格式(list)
- 图神经网络(GNN)必读论文及最新进展跟踪
- 阿里云 MaxCompute Tunnel Upload 上传典型问题整理
- easyui手风琴菜单
- php解决缓慢http请求,php CURL 服务器响应慢的问题
- Driller源码阅读笔记(二)
- 重庆华侨城携手T.M.D PCP 发财潮流文化节2.0国庆重磅来袭
- DCC尺寸链计算与公差仿真分析软件 软件应用案例以及授权文件
- 朗强:紧跟时代步伐!HDMI分布式矩阵可以通过手机来控制!
- san mysql,高性能MySQL :应该用SAN吗?
- Android Studio 4.1 发布
- 【转】14年从业老兵分享万字长文MMO成功秘诀
- 支付系统设计四:轮询扣款设计04-整体设计
- dovecot主要配置文件