面向对象的设计原则
Note: This article has been written with the assistance of AI.
单一职责原则(SRP)
一个类应该只有一个引起它变化的原因。
通俗地讲,就是一个类只负责一项职责或功能。不要设计“万能”类,把不相关的功能塞在一起。如果一个类承担了太多的职责,就等于把这些职责耦合在了一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。
核心思想: 高内聚,低耦合。
违反 SRP 的类会带来很多问题:
- 可读性差: 一个庞大的类,代码量多,逻辑复杂,难以理解和维护。
- 难以维护和修改: 当你需要修改某个功能时,你不得不在这个庞大的类中进行,很容易“牵一发而动全身”,导致意想不到的 Bug。
- 难以复用: 你只想复用这个类中的某一个功能,但却不得不把整个“庞然大物”都引入进来。
- 稳定性差: 一个职责的变更,可能会影响到其他不相关功能的正常运行。
违反 SRP 的设计
假设我们有一个 User 类,它负责处理用户的所有事情:
// 违反单一职责原则的类
public class User {
private String name;
private String email;
// 构造函数、getter、setter 省略...
// 职责1:用户属性相关的业务逻辑(这是合理的)
public boolean isValid() {
return email != null && email.contains("@");
}
// 职责2:数据持久化 - 将用户保存到数据库
public void saveToDatabase() {
// 连接数据库,执行 INSERT 语句...
System.out.println("用户 " + name + " 已保存到数据库。");
}
// 职责3:通知 - 发送欢迎邮件
public void sendWelcomeEmail() {
// 连接邮件服务器,发送邮件...
System.out.println("已发送欢迎邮件给 " + email);
}
}
遵循 SRP 的改进设计
我们将上述三个职责拆分到三个不同的类中:
// 职责1:用户实体模型 - 只负责用户数据和基础验证
public class User {
private String name;
private String email;
// ... 构造函数、getter、setter ...
public boolean isValid() {
return email != null && email.contains("@");
}
}
// 职责2:用户数据访问 - 只负责数据库操作
public class UserRepository {
public void save(User user) {
// 连接数据库,执行 INSERT 语句...
System.out.println("用户 " + user.getName() + " 已保存到数据库。");
}
// 还可以有 findById, delete 等方法
}
// 职责3:通知服务 - 只负责发送消息
public class NotificationService {
public void sendWelcomeEmail(User user) {
// 连接邮件服务器,发送邮件...
System.out.println("已发送欢迎邮件给 " + user.getEmail());
}
// 未来可以轻松添加新方法,而不影响 User 和 UserRepository
public void sendSms(User user) {
// 发送短信的逻辑
}
}
开放封闭原则(OCP)
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
这个原则听起来可能有些矛盾,但它表达了一个非常深刻的思想:
- 对扩展开放(Open for Extension): 当需求发生变化时,我们可以通过添加新的代码来扩展模块的行为,从而满足新的需求。
- 对修改关闭(Closed for Modification): 扩展模块行为时,不应该修改原有的、已经测试通过的、正在稳定运行的代码。
核心思想: 通过抽象构建框架,通过实现扩展细节。用抽象定义结构,用具体实现来应对变化。
违反 OCP 会带来严重的问题:
- 稳定性风险: 修改已有的、稳定的代码,就像在已经建好的大楼上动地基,极易引入新的 Bug,导致系统变得脆弱。
- 测试负担重: 每次修改代码后,都需要对修改过的模块进行全面的回归测试,以确保没有破坏现有功能。
- 难以扩展: 系统会变得僵硬,添加新功能变得越来越困难,因为牵一发而动全身。
违反 OCP 的设计
假设我们有一个 AreaCalculator 类,用于计算一组图形的总面积。
// 违反开放封闭原则的设计
public class AreaCalculator {
// 计算所有图形的总面积
public double calculateTotalArea(Object[] shapes) {
double totalArea = 0;
for (Object shape : shapes) {
// 问题核心:如果新增一种图形,就必须修改这里的 if-else 逻辑
if (shape instanceof Rectangle) {
Rectangle rect = (Rectangle) shape;
totalArea += rect.getWidth() * rect.getHeight();
} else if (shape instanceof Circle) {
Circle circle = (Circle) shape;
totalArea += Math.PI * circle.getRadius() * circle.getRadius();
}
// 如果需要添加三角形,必须在这里添加新的 else if 分支...
// else if (shape instanceof Triangle) { ... }
}
return totalArea;
}
}
// 矩形类
public class Rectangle {
private double width;
private double height;
// ... 构造函数、getter ...
}
// 圆形类
public class Circle {
private double radius;
// ... 构造函数、getter ...
}
遵循 OCP 的改进设计
解决方案是使用抽象。我们创建一个抽象的 Shape 接口,让所有具体的图形类都实现这个接口,并自己负责计算自己的面积。这样,面积计算器就不再需要关心具体的图形类型,它只依赖于一个稳定的抽象。
// 步骤1:定义抽象接口
public interface Shape {
double calculateArea(); // 所有图形都必须实现这个方法来计算自己的面积
}
// 步骤2:让具体图形类实现接口
public class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
}
public class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
// 步骤3:重构面积计算器,使其依赖于抽象(Shape),而非具体实现(Rectangle, Circle)
public class AreaCalculator {
// 现在这个方法对修改是“关闭”的。无论未来有多少新图形,都无需修改它。
public double calculateTotalArea(List<Shape> shapes) {
double totalArea = 0;
for (Shape shape : shapes) {
// 直接调用抽象方法,多态机制会自动分发到具体实现
totalArea += shape.calculateArea();
}
return totalArea;
}
}
里氏替换原则(LSP)
所有引用基类(父类)的地方必须能够透明地使用其子类的对象。
更通俗的解释是:子类必须能够完全替换掉它们的父类,并且替换后不会产生任何错误或异常行为。 换句话说,如果你把程序中的父类对象全部替换成它的某个子类对象,程序应该仍然能够正常运行。
这个原则是以计算机科学家 Barbara Liskov 的名字命名的。
违反 LSP 会破坏面向对象的继承体系,导致严重问题:
- 破坏多态性: 多态是面向对象的核心特性,而 LSP 是保证多态能够正确工作的基石。如果子类不能替换父类,那么基于父类设计的代码将无法安全地使用子类。
- 引发运行时错误: 在替换后,程序可能会抛出异常、产生错误结果或进入异常状态。
- 增加代码复杂度: 使用父类对象时,开发者不得不担心具体是哪个子类,需要做类型检查和特殊处理,这违背了面向抽象编程的原则。
里氏替换原则不仅仅是语法上的"is-a"关系,更重要的是行为上的兼容性。"is-a"关系不仅仅是分类学上的,更重要的是行为上的可替换性。。它要求子类在继承父类时:
- 不强化前置条件(子类对输入的要求不能比父类更严格)
- 不弱化后置条件(子类对输出的承诺不能比父类更少)
- 保持父类的约束条件(如不变量)
示例1:违反LSP的经典"正方形/矩形"问题
// 长方形类
class Rectangle {
private double width;
private double height;
public double getWidth() { return width; }
public double getHeight() { return height; }
public void setWidth(double width) { this.width = width; }
public void setHeight(double height) { this.height = height; }
public double calculateArea() {
return width * height;
}
}
// 正方形类 - 从数学上说,正方形"is-a"长方形
class Square extends Rectangle {
@Override
public void setWidth(double width) {
// 正方形需要保持宽高相等
super.setWidth(width);
super.setHeight(width); // 这里修改了高度!
}
@Override
public void setHeight(double height) {
// 正方形需要保持宽高相等
super.setHeight(height);
super.setWidth(height); // 这里修改了宽度!
}
}
现在考虑一个使用 Rectangle 的客户端代码:
public class Test {
// 这个方法期望接收一个Rectangle对象
public static void testRectangle(Rectangle rect) {
rect.setWidth(5);
rect.setHeight(4);
// 根据长方形特性,面积应该是20
// 但如果传入的是Square对象,面积会是16!
System.out.println("Expected area: 20, Actual area: " + rect.calculateArea());
// 断言检查 - 对于Square会失败
assert rect.calculateArea() == 20 : "违反长方形行为约定!";
}
public static void main(String[] args) {
Rectangle rect = new Rectangle();
testRectangle(rect); // 正常工作:面积=20
Rectangle square = new Square(); // 用子类替换父类
testRectangle(square); // 出现问题:面积=16!
}
}
Square 虽然语法上继承自 Rectangle,但它的行为与 Rectangle 的约定不一致。Rectangle 的 setWidth() 和 setHeight() 应该是独立操作的,而 Square 修改了这个行为。因此,Square 不能透明地替换 Rectangle。
对于"正方形/矩形"问题,更好的设计是不要使用继承:
// 使用组合而不是继承
interface Shape {
double calculateArea();
}
class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
// getter方法...
}
class Square implements Shape {
private double side;
public Square(double side) {
this.side = side;
}
@Override
public double calculateArea() {
return side * side;
}
// getter方法...
}
示例2:违反LSP的"企鹅/鸟"问题
// 鸟类
class Bird {
public void fly() {
System.out.println("I can fly");
}
public void eat() {
System.out.println("I can eat");
}
}
// 燕子类
class Swallow extends Bird {
// 燕子会飞,符合Bird的行为
}
// 企鹅类 - 从生物学上说,企鹅"is-a"鸟
class Penguin extends Bird {
@Override
public void fly() {
// 但企鹅不会飞!这里抛出异常或什么都不做都违反了LSP
throw new UnsupportedOperationException("Penguins can't fly!");
}
}
考虑使用 Bird 的客户端代码:
public class BirdTest {
public static void makeBirdFly(Bird bird) {
// 这个方法期望所有Bird都能fly
bird.fly(); // 如果传入Penguin,这里会抛出异常!
}
public static void main(String[] args) {
Bird swallow = new Swallow();
makeBirdFly(swallow); // 正常工作
Bird penguin = new Penguin(); // 用子类替换父类
makeBirdFly(penguin); // 抛出异常!违反LSP
}
}
对于"企鹅/鸟"问题,通过接口隔离来遵循LSP:
// 细化的接口
interface Bird {
void eat();
}
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
// 燕子实现Flyable
class Swallow implements Bird, Flyable {
@Override
public void eat() { System.out.println("Swallow eating"); }
@Override
public void fly() { System.out.println("Swallow flying"); }
}
// 企鹅实现Swimmable但不实现Flyable
class Penguin implements Bird, Swimmable {
@Override
public void eat() { System.out.println("Penguin eating"); }
@Override
public void swim() { System.out.println("Penguin swimming"); }
}
现在客户端可以安全地使用:
public class BirdTest {
public static void makeBirdFly(Flyable flyableBird) {
flyableBird.fly(); // 这里保证传入的对象一定能fly
}
public static void makeBirdSwim(Swimmable swimmableBird) {
swimmableBird.swim(); // 这里保证传入的对象一定能swim
}
}
应用 LSP 总结
- 子类方法的前置条件不能强于父类:子类对输入参数的要求不能比父类更严格。
- 子类方法的后置条件不能弱于父类:子类对输出结果的承诺不能比父类更少。
- 子类不应该抛出父类没有声明的异常:或者抛出的异常应该是父类异常的子类。
- 子类应该保持父类的约束条件:如某些字段的取值范围等。
- 当发现需要频繁进行类型检查时(如
if (obj instanceof SomeSubclass)),这通常是违反LSP的信号。
接口隔离原则(ISP)
客户端不应该被迫依赖于它不使用的接口。或者说,一个类对另一个类的依赖应该建立在最小的接口上。
通俗地讲:不要创建庞大臃肿的接口,而应该将接口拆分成更小、更具体的接口,这样客户端只需要知道它们感兴趣的方法。
违反 ISP 会带来很多问题:
- 接口污染: 客户端被迫实现它们根本不需要的方法,这违反了单一职责原则。
- 代码冗余: 对于不需要的方法,客户端通常只能提供空实现或抛出异常,这会产生大量无用的代码。
- 耦合性增加: 客户端与不需要的方法产生了依赖,当接口发生变化时,即使客户端不关心那些方法,也不得不重新编译和部署。
- 难以理解和维护: 庞大的接口难以理解,也很难知道一个类到底实现了哪些真正有用的功能。
违反 ISP 的设计
假设我们有一个庞大的 Animal 接口,包含了所有动物可能具有的行为:
// 一个"臃肿"的接口,违反了接口隔离原则
public interface Animal {
void eat();
void sleep();
void fly(); // 问题:不是所有动物都会飞
void swim(); // 问题:不是所有动物都会游泳
void run(); // 问题:不是所有动物都会跑
}
// 鸟类实现 - 被迫实现所有方法
public class Bird implements Animal {
@Override
public void eat() {
System.out.println("Bird is eating");
}
@Override
public void sleep() {
System.out.println("Bird is sleeping");
}
@Override
public void fly() {
System.out.println("Bird is flying"); // 鸟确实会飞
}
@Override
public void swim() {
// 但大多数鸟不会游泳!被迫提供空实现或抛出异常
// throw new UnsupportedOperationException("Birds don't swim!");
// 或者什么都不做,但这违反了"最小惊讶原则"
}
@Override
public void run() {
System.out.println("Bird is running");
}
}
// 鱼类实现 - 同样被迫实现所有方法
public class Fish implements Animal {
@Override
public void eat() {
System.out.println("Fish is eating");
}
@Override
public void sleep() {
System.out.println("Fish is sleeping");
}
@Override
public void fly() {
// 鱼根本不会飞!被迫提供无意义的实现
throw new UnsupportedOperationException("Fish can't fly!");
}
@Override
public void swim() {
System.out.println("Fish is swimming"); // 鱼确实会游泳
}
@Override
public void run() {
// 鱼不会跑!
throw new UnsupportedOperationException("Fish can't run!");
}
}
遵循 ISP 的改进设计
// 拆分成多个专门的接口
// 基本生物接口
public interface LivingBeing {
void eat();
void sleep();
}
// 飞行能力接口
public interface Flyable {
void fly();
}
// 游泳能力接口
public interface Swimmable {
void swim();
}
// 奔跑能力接口
public interface Runnable {
void run();
}
与其提供一个什么都做的万能接口,不如提供多个专注的专用接口。
依赖倒置原则(DIP)
它的核心定义包含两个部分:
- 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
- 抽象不应该依赖于细节,细节应该依赖于抽象
这里的"倒置"指的是与传统依赖方向的对比。在传统设计中,高层模块直接依赖低层模块,而DIP将这种依赖关系"倒置"了。
违反 DIP 会导致的问题:
- 紧耦合:高层模块与低层模块紧密耦合,难以独立变化
- 难以测试:无法轻松替换依赖进行单元测试
- 难以扩展:添加新功能需要修改高层模块代码
- 违反开闭原则:对扩展不开放,对修改不关闭
- 高层模块:包含核心业务逻辑、策略和应用程序规则的模块
- 低层模块:包含具体实现、工具函数、基础设施细节的模块
- 抽象:接口或抽象类,定义契约而不涉及具体实现
违反 DIP 的设计
// 低层模块 - 具体实现
public class EmailService {
public void sendEmail(String message, String recipient) {
System.out.println("发送邮件给 " + recipient + ": " + message);
// 实际的邮件发送逻辑...
}
}
public class SmsService {
public void sendSms(String message, String phoneNumber) {
System.out.println("发送短信给 " + phoneNumber + ": " + message);
// 实际的短信发送逻辑...
}
}
// 高层模块 - 业务逻辑
public class NotificationService {
// 问题:高层模块直接依赖于低层模块的具体实现
private EmailService emailService;
private SmsService smsService;
public NotificationService() {
// 紧耦合:在构造函数中直接创建具体实例
this.emailService = new EmailService();
this.smsService = new SmsService();
}
public void sendNotification(String message, String user, String type) {
if ("email".equals(type)) {
emailService.sendEmail(message, user);
} else if ("sms".equals(type)) {
smsService.sendSms(message, user);
}
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
NotificationService notification = new NotificationService();
notification.sendNotification("Hello", "user@example.com", "email");
}
}
遵循 DIP 的改进设计
// 步骤1:定义抽象(接口)
public interface MessageService {
void send(String message, String recipient);
boolean supports(String type); // 支持的消息类型
}
// 步骤2:低层模块实现抽象
public class EmailService implements MessageService {
@Override
public void send(String message, String recipient) {
System.out.println("发送邮件给 " + recipient + ": " + message);
}
@Override
public boolean supports(String type) {
return "email".equals(type);
}
}
public class SmsService implements MessageService {
@Override
public void send(String message, String recipient) {
System.out.println("发送短信给 " + recipient + ": " + message);
}
@Override
public boolean supports(String type) {
return "sms".equals(type);
}
}
public class WeChatService implements MessageService {
@Override
public void send(String message, String recipient) {
System.out.println("发送微信消息给 " + recipient + ": " + message);
}
@Override
public boolean supports(String type) {
return "wechat".equals(type);
}
}
// 步骤3:高层模块依赖于抽象
public class NotificationService {
// 高层模块依赖于抽象(接口),而不是具体实现
private List<MessageService> messageServices;
// 通过构造函数注入依赖 - 这就是依赖注入(DI)
public NotificationService(List<MessageService> messageServices) {
this.messageServices = messageServices;
}
public void sendNotification(String message, String recipient, String type) {
for (MessageService service : messageServices) {
if (service.supports(type)) {
service.send(message, recipient);
return;
}
}
throw new IllegalArgumentException("不支持的消息类型: " + type);
}
// 可以轻松添加新功能而不修改现有代码
public void broadcast(String message, String type) {
for (MessageService service : messageServices) {
if (service.supports(type)) {
service.send(message, "all users");
}
}
}
}
依赖注入的三种方式
构造函数注入
public class NotificationService {
private final MessageService messageService;
// 依赖通过构造函数注入
public NotificationService(MessageService messageService) {
this.messageService = messageService;
}
}
Setter方法注入
public class NotificationService {
private MessageService messageService;
// 依赖通过setter方法注入
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
}
接口注入
public interface MessageServiceAware {
void setMessageService(MessageService messageService);
}
public class NotificationService implements MessageServiceAware {
private MessageService messageService;
@Override
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
}
最少知识原则 (Law of Demeter)
一个对象应该对其他对象有最少的了解。或者说,只与直接的朋友通信。
通俗地讲:不要和陌生人说话,只和你的直接朋友通信。
这里的"朋友"指的是:
- 当前对象本身 (
this) - 以参数形式传入到当前对象方法中的对象
- 当前对象的成员变量
- 如果当前对象的成员变量是一个集合,那么集合中的元素也是朋友
- 当前对象所创建的对象
违反 LoD 会带来很多问题:
- 耦合度增高:对象之间关系复杂,形成蜘蛛网式的依赖
- 难以维护:一个类的修改会影响到很多其他类
- 难以复用:类之间紧密耦合,难以单独复用
- 可测试性差:需要构建复杂的依赖关系才能进行测试
核心规则:"只使用一个点"
一个简单的判断方法是:在方法中,应该尽量避免使用多个连续的.(点)来访问对象。通常,只使用一个点是相对安全的。
违反 LoD 的设计
// 员工类
public class Employee {
private String name;
private Department department;
public Employee(String name, Department department) {
this.name = name;
this.department = department;
}
// getter方法
public String getName() { return name; }
public Department getDepartment() { return department; }
}
// 部门类
public class Department {
private String name;
private Company company;
public Department(String name, Company company) {
this.name = name;
this.company = company;
}
// getter方法
public String getName() { return name; }
public Company getCompany() { return company; }
}
// 公司类
public class Company {
private String name;
private Address address;
public Company(String name, Address address) {
this.name = name;
this.address = address;
}
// getter方法
public String getName() { return name; }
public Address getAddress() { return address; }
}
// 地址类
public class Address {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// getter方法
public String getCity() { return city; }
public String getStreet() { return street; }
}
// 报告生成类 - 违反迪米特法则!
public class ReportGenerator {
// 问题:这个方法知道了太多它不应该知道的细节
public void generateEmployeeReport(Employee employee) {
// 违反LoD:使用了多个连续的点,深入了解了Employee的内部结构
System.out.println("员工姓名: " + employee.getName());
System.out.println("部门: " + employee.getDepartment().getName()); // 第一个点之后又一个点
System.out.println("公司: " + employee.getDepartment().getCompany().getName()); // 两个连续的点
System.out.println("公司地址: " + employee.getDepartment().getCompany().getAddress().getCity()); // 三个连续的点!
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Address address = new Address("北京", "海淀区中关村");
Company company = new Company("某科技公司", address);
Department department = new Department("技术部", company);
Employee employee = new Employee("张三", department);
ReportGenerator generator = new ReportGenerator();
generator.generateEmployeeReport(employee);
}
}
ReportGenerator知道了Employee、Department、Company、Address的所有内部结构- 如果
Company类的结构发生变化(比如把address字段改名),ReportGenerator也需要修改 - 形成了紧密的耦合,
ReportGenerator依赖于整个对象链
遵循 LoD 的改进设计
// 员工类 - 添加业务方法,封装内部细节
public class Employee {
private String name;
private Department department;
public Employee(String name, Department department) {
this.name = name;
this.department = department;
}
public String getName() { return name; }
// 提供业务方法,而不是简单的getter
public String getDepartmentName() {
return department.getDepartmentName();
}
public String getCompanyName() {
return department.getCompanyName();
}
public String getCompanyCity() {
return department.getCompanyCity();
}
// 或者提供一个综合的方法
public String getWorkInfo() {
return String.format("%s在%s的%s工作",
name, getCompanyCity(), getDepartmentName());
}
}
// 部门类 - 同样封装内部细节
public class Department {
private String name;
private Company company;
public Department(String name, Company company) {
this.name = name;
this.company = company;
}
public String getDepartmentName() { return name; }
// 封装对Company的访问
public String getCompanyName() {
return company.getCompanyName();
}
public String getCompanyCity() {
return company.getCompanyCity();
}
}
// 公司类 - 封装内部细节
public class Company {
private String name;
private Address address;
public Company(String name, Address address) {
this.name = name;
this.address = address;
}
public String getCompanyName() { return name; }
// 封装对Address的访问
public String getCompanyCity() {
return address.getCity();
}
}
// 地址类保持不变
public class Address {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
public String getCity() { return city; }
public String getStreet() { return street; }
}
// 改进的报告生成类 - 遵循 LoD
public class ReportGenerator {
// 现在这个方法只与直接朋友(Employee)通信
public void generateEmployeeReport(Employee employee) {
System.out.println("员工姓名: " + employee.getName());
System.out.println("部门: " + employee.getDepartmentName()); // 只有一个点
System.out.println("公司: " + employee.getCompanyName()); // 只有一个点
System.out.println("工作地点: " + employee.getCompanyCity()); // 只有一个点
// 或者使用综合方法
System.out.println("工作信息: " + employee.getWorkInfo());
}
}
外观模式
// 员工信息服务(外观类)
public class EmployeeInfoService {
private Employee employee;
public EmployeeInfoService(Employee employee) {
this.employee = employee;
}
public String getBasicInfo() {
return employee.getName();
}
public String getWorkInfo() {
return String.format("在%s的%s部门工作",
employee.getCompanyCity(), employee.getDepartmentName());
}
public String getFullReport() {
return String.format("员工%s,在%s的%s部门,属于%s公司",
employee.getName(),
employee.getCompanyCity(),
employee.getDepartmentName(),
employee.getCompanyName());
}
}
// 简化的报告生成类
public class ReportGenerator {
public void generateEmployeeReport(EmployeeInfoService infoService) {
System.out.println("基本信息: " + infoService.getBasicInfo());
System.out.println("工作信息: " + infoService.getWorkInfo());
System.out.println("完整报告: " + infoService.getFullReport());
}
}
让类保持"害羞" - 不要暴露太多内部细节,也不要打听太多别人的事情。

浙公网安备 33010602011771号