文章目录
在学习 Android 开发的过程中,经常会听到"设计模式""MVC""MVP""高内聚低耦合"等概念。刚开始接触这些内容时,很容易觉得它们十分抽象,似乎都是大型项目才需要考虑的问题。但当项目逐渐变大之后,Activity 越写越长、逻辑越来越乱、修改一个功能却影响多个地方时,就会发现这些思想并不是为了增加代码复杂度,而是为了让代码更容易维护、更容易扩展。
设计模式并不是固定的模板,而是一套经过大量项目实践总结出来的经验。真正需要先掌握的,其实是设计模式背后的六大设计原则。很多常见的设计模式,本质上都是这些原则的具体体现。
为什么要学习设计原则
初学 Java 时,我们更多关注的是语法:类怎么写、对象怎么创建、方法怎么调用。但软件工程关注的是另一件事:当代码不断增加时,它还能不能继续维护。例如,一个天气应用最初只有几十行代码,所有逻辑都写在 MainActivity 中没有任何问题。但随着网络请求、JSON 解析、数据库、本地缓存、通知等功能不断加入,一个 Activity 可能会变成几百甚至上千行代码。此时,真正的问题不再是代码能不能运行,而是代码是否容易修改、是否容易测试、是否容易扩展。设计原则就是为了解决这些问题而提出的。
单一职责原则
单一职责原则(Single Responsibility Principle,SRP)的核心思想是:一个类只负责一件事情。
很多初学者喜欢把所有功能都放到一个类中,例如既负责下载天气数据,又负责解析 JSON,又负责保存数据库,还负责更新界面。这样的类功能越来越多,任何一个地方修改都有可能影响其他逻辑。
更合理的做法是把不同职责拆分出去。
java
class WeatherService {
public String requestWeather() {
return "晴";
}
}
class WeatherDatabase {
public void save(String weather) {
System.out.println("保存天气:" + weather);
}
}
public class Main {
public static void main(String[] args) {
WeatherService service = new WeatherService();
WeatherDatabase database = new WeatherDatabase();
String weather = service.requestWeather();
database.save(weather);
}
}
WeatherService 只负责获取天气,WeatherDatabase 只负责存储数据。以后无论网络接口变化还是数据库变化,都可以分别修改,而不会互相影响。
开闭原则
开闭原则(Open-Closed Principle,OCP)强调:对扩展开放,对修改关闭。
假设有一个支付功能,最开始只支持微信支付。
java
class WeChatPay {
public void pay() {
System.out.println("微信支付");
}
}
后来需要支持支付宝,如果直接修改原来的类,就可能影响已经正常运行的功能。
更好的方式是定义统一接口。
java
interface PayStrategy {
void pay();
}
class WeChatPay implements PayStrategy {
@Override
public void pay() {
System.out.println("微信支付");
}
}
class AliPay implements PayStrategy {
@Override
public void pay() {
System.out.println("支付宝支付");
}
}
public class Main {
public static void main(String[] args) {
PayStrategy pay = new AliPay();
pay.pay();
}
}
以后新增银联支付、数字人民币支付,只需要增加新的实现类,而不需要修改已经存在的代码。
里氏替换原则
里氏替换原则(Liskov Substitution Principle,LSP)的意思是:子类必须能够替换父类使用。
例如:
java
class Animal {
public void move() {
System.out.println("动物移动");
}
}
class Dog extends Animal {
@Override
public void move() {
System.out.println("狗在奔跑");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.move();
}
}
程序并不关心具体是 Dog 还是其他动物,只要它们都遵循 Animal 的行为约定即可。
如果子类改变了父类原本的语义,例如父类表示"可以移动",而子类变成"抛出异常不能移动",就破坏了里氏替换原则。
接口隔离原则
接口隔离原则(Interface Segregation Principle,ISP)要求:不要让类依赖自己不需要的方法。
例如,一个接口同时定义了打印、扫描、传真。
java
interface Machine {
void print();
void scan();
void fax();
}
如果某个设备只是普通打印机,它仍然必须实现 scan() 和 fax(),即使这些功能根本不存在。
更合理的设计是拆分接口。
java
interface Printer {
void print();
}
interface Scanner {
void scan();
}
class SimplePrinter implements Printer {
@Override
public void print() {
System.out.println("打印文档");
}
}
这样每个类只实现自己真正需要的能力。
依赖倒置原则
依赖倒置原则(Dependency Inversion Principle,DIP)强调:依赖抽象,而不是依赖具体实现。
错误的方式:
java
class Keyboard {
public void input() {
System.out.println("键盘输入");
}
}
class Computer {
private Keyboard keyboard = new Keyboard();
public void work() {
keyboard.input();
}
}
Computer 被固定绑定到了 Keyboard,以后如果改成 BluetoothKeyboard,就必须修改 Computer。
更好的方式:
java
interface InputDevice {
void input();
}
class Keyboard implements InputDevice {
@Override
public void input() {
System.out.println("键盘输入");
}
}
class BluetoothKeyboard implements InputDevice {
@Override
public void input() {
System.out.println("蓝牙键盘输入");
}
}
class Computer {
private InputDevice device;
public Computer(InputDevice device) {
this.device = device;
}
public void work() {
device.input();
}
}
public class Main {
public static void main(String[] args) {
Computer computer = new Computer(new BluetoothKeyboard());
computer.work();
}
}
Computer 只依赖 InputDevice 抽象接口,具体使用哪种设备由外部决定。
迪米特法则
迪米特法则(Law of Demeter)又称最少知识原则,其思想是:一个对象应当尽量少了解其他对象的内部细节。
例如:
java
class Engine {
public void start() {
System.out.println("发动机启动");
}
}
class Car {
private Engine engine = new Engine();
public void start() {
engine.start();
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.start();
}
}
外部代码只需要调用 car.start(),不需要知道 Car 内部使用了 Engine,也不需要直接操作发动机对象。
这样可以减少对象之间的耦合,提高封装性。
常见设计模式与设计原则的关系
学习设计模式时,不需要一开始就记住二十多种模式。更重要的是理解它们是在解决什么问题。
例如:
• 工厂模式:体现开闭原则、依赖倒置原则
• 单例模式:控制对象唯一性
• 观察者模式:降低对象之间的耦合
• 策略模式:符合开闭原则
• 适配器模式:兼容不同接口
可以发现,设计模式并不是独立存在的,它们往往是多个设计原则共同作用后的结果。