设计模式在我们的日常软件开发中无处不在。它们帮助我们编写更具可扩展性和可读性的代码。
今天,结合我的实际工作场景和源码示例,我将和你探讨工作中这 7 种最常用的设计模式,希望能对你有所帮助。
1. 单例模式
单例模式确保一个类只有一个实例,通常用于管理配置、缓存、线程池等共享资源。
代码实现 1:双重检查锁定
public class Singleton { privatestaticvolatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }这是单例模式的标准写法,既保证了线程安全,又避免了性能损失。
代码解释:
private static volatile Singleton instance;volatile关键字确保了instance变量的可见性。当一个线程修改了instance的值,其他线程可以立即看到最新的值。同时,
volatile还能防止指令重排序。在创建Singleton实例时,new Singleton()操作并非原子操作。它包含三个步骤:分配内存空间、初始化对象、将对象引用赋值给instance。如果没有volatile,可能会发生指令重排序,导致其他线程获取到一个尚未完全初始化的instance对象。private Singleton()私有构造函数确保无法从外部直接实例化
Singleton类,保证了单例模式的唯一性。public static Singleton getInstance()首先检查
instance是否为null。如果不为null,直接返回实例,避免进入同步代码块,提高了性能。如果
instance为null,进入同步代码块。synchronized (Singleton.class)语句锁定Singleton类,确保同一时间只有一个线程能进入创建实例的代码块。再次检查
instance是否为null。这是为了防止多个线程同时通过第一次检查,进入同步代码块并重复创建实例。当
instance为null时,执行instance = new Singleton();创建单例实例。
代码实现 2:枚举单例模式
这是 Effective Java 中更推荐的单例模式实现方式。
public enum SingletonEnum { INSTANCE; public void doSomething() { System.out.println("This is a singleton instance doing something."); } }代码解释:
枚举类型是 Java 中一种特殊的类。它保证了实例的唯一性,并且天生是线程安全的。
INSTANCE是SingletonEnum枚举的唯一实例。你可以通过SingletonEnum.INSTANCE获取该单例。你可以在枚举中添加自定义方法,如
doSomething(),来实现单例需要完成的业务逻辑。枚举单例可以防止反射和序列化破坏单例,因为 Java 规范保证了枚举类型的实例化是唯一的,且在序列化和反序列化过程中不会创建新实例。
JDK 中的应用:
java.lang.Runtime.getRuntime()java.util.logging.Logger
Spring 中的应用
默认情况下,Spring 的Bean是单例模式。你可以通过使用@Scope("prototype")将其更改为原型模式。
2. 工厂模式
工厂模式用于封装对象的创建逻辑,特别是在类实例化过程复杂时,可以降低耦合度。
代码实现:简单工厂
以支付系统为例,不同的支付方式需要不同的对象。
// 抽象产品:交通工具 (Vehicle) abstractclass Vehicle { public abstract void drive(); } class Car extends Vehicle { @Override public void drive() { System.out.println("Driving a car."); } } class Bicycle extends Vehicle { @Override public void drive() { System.out.println("Riding a bicycle."); } } class Truck extends Vehicle { @Override public void drive() { System.out.println("Driving a truck."); } } // 工厂类:交通工具工厂 (Vehicle factories) class VehicleFactory { public static Vehicle createVehicle(String vehicleType) { switch (vehicleType) { case"car": returnnew Car(); case"bicycle": returnnew Bicycle(); case"truck": returnnew Truck(); default: thrownew IllegalArgumentException("Invalid vehicle type: " + vehicleType); } } } // 测试类 publicclass FactoryPatternExample { public static void main(String[] args) { // 创建一辆汽车并驾驶 Vehicle car = VehicleFactory.createVehicle("car"); car.drive(); // 创建一辆自行车并骑行 Vehicle bicycle = VehicleFactory.createVehicle("bicycle"); bicycle.drive(); // 创建一辆卡车并驾驶 Vehicle truck = VehicleFactory.createVehicle("truck"); truck.drive(); } }代码解释:
当你需要制造交通工具时,不需要直接调用
Car、Bicycle或Truck的构造函数,而是调用VehicleFactory.createVehicle()方法,并传入你想要制造的交通工具类型。工厂会根据你提供的类型创建并返回相应的交通工具实例。
你可以通过调用
drive()方法来使用制造出来的交通工具。
优点:
代码解耦:交通工具的创建逻辑和使用逻辑分离。用户不需要关心具体的交通工具创建过程,只需要告诉工厂想要什么类型的交通工具。
易于扩展:如果你想添加新的交通工具类型,只需创建一个新的具体交通工具类并继承
Vehicle,然后在VehicleFactory的createVehicle()方法中添加相应的创建逻辑即可。
例如,如果你想添加Motorcycle(摩托车)类型,可以这样做:
class Motorcycle extends Vehicle { @Override public void drive() { System.out.println("Riding a motorcycle."); } }然后在VehicleFactory中添加相应的逻辑:
class VehicleFactory { public static Vehicle createVehicle(String vehicleType) { switch (vehicleType) { case"car": returnnew Car(); case"bicycle": returnnew Bicycle(); case"truck": returnnew Truck(); case"motorcycle": returnnew Motorcycle(); // 添加摩托车创建逻辑 default: thrownew IllegalArgumentException("Invalid vehicle type: " + vehicleType); } } }这个例子展示了工厂模式如何通过工厂类将对象的创建和使用分离,使得代码更具可扩展性和可维护性,同时也方便了新产品类型的添加。
如果你想进一步避免在工厂类中使用switch或if-else语句,可以使用反射机制如下:
class VehicleFactory { public static Vehicle createVehicle(Class<? extends Vehicle> vehicleClass) { try { Constructor<? extends Vehicle> constructor = vehicleClass.getDeclaredConstructor(); return constructor.newInstance(); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { thrownew RuntimeException("Failed to create vehicle: " + e.getMessage()); } } } publicclass FactoryPatternExample { public static void main(String[] args) { // 创建一辆汽车并驾驶 Vehicle car = VehicleFactory.createVehicle(Car.class); car.drive(); // 创建一辆自行车并骑行 Vehicle bicycle = VehicleFactory.createVehicle(Bicycle.class); bicycle.drive(); // 创建一辆卡车并驾驶 Vehicle truck = VehicleFactory.createVehicle(Truck.class); truck.drive(); } }这种方法利用反射避免了繁琐的条件判断,提高了工厂的可扩展性。当添加新的交通工具类型时,无需修改工厂类代码。你只需要将新交通工具类的Class对象传递给工厂类的createVehicle()方法即可。
JDK 中的应用:
java.util.Calendar.getInstance()javax.xml.parsers.DocumentBuilderFactory.newInstance()
Spring 中的应用:
BeanFactory和ApplicationContext都是工厂模式的体现。
3. 建造者模式
建造者模式用于创建复杂对象,特别是当对象拥有多个可选参数时。
代码实现:组装一台电脑
// 产品类:电脑 (Computer) class Computer { private String cpu; private String memory; private String storage; private String graphicsCard; private String operatingSystem; // 私有构造函数,只能通过 Builder 创建对象 private Computer(Builder builder) { this.cpu = builder.cpu; this.memory = builder.memory; this.storage = builder.storage; this.graphicsCard = builder.graphicsCard; this.operatingSystem = builder.operatingSystem; } // 静态内部类 Builder publicstaticclass Builder { private String cpu; private String memory; private String storage; private String graphicsCard; private String operatingSystem; // 必填参数通过构造函数传递 public Builder(String cpu, String memory) { this.cpu = cpu; this.memory = memory; } // 设置可选参数的方法 public Builder storage(String storage) { this.storage = storage; returnthis; } public Builder graphicsCard(String graphicsCard) { this.graphicsCard = graphicsCard; returnthis; } public Builder operatingSystem(String operatingSystem) { this.operatingSystem = operatingSystem; returnthis; } // 构建最终的 Computer 对象 public Computer build() { returnnew Computer(this); } } // 显示电脑配置信息 public void showConfiguration() { System.out.println("CPU: " + cpu); System.out.println("Memory: " + memory); System.out.println("Storage: " + storage); System.out.println("Graphics Card: " + graphicsCard); System.out.println("Operating System: " + operatingSystem); } } // 测试类 publicclass BuilderPatternExample { public static void main(String[] args) { // 使用建造者模式构建电脑 Computer computer = new Computer.Builder("Intel i7", "16GB") .storage("1TB SSD") .graphicsCard("NVIDIA RTX 4060") .operatingSystem("Windows 10") .build(); // 显示电脑配置信息 computer.showConfiguration(); } }JDK 中的应用:
StringBuilderStream.Builder
Spring 中的应用:
BeanDefinitionBuilder用于构建BeanDefinition对象,这是 Spring 容器管理 Bean 的核心组件。UriComponentsBuilder用于构建 URI。
4. 策略模式
策略模式将不同的算法封装成独立的类,并允许在运行时选择不同的策略。
代码实现:出行方式的选择
这次我们以出行方式的选择为例。不同的出行方式有不同的成本和速度,根据用户需求可以选择不同的出行策略:
// 策略接口:出行策略 (Travel Strategy) interface TravelStrategy { void travel(); double calculateCost(double distance); double calculateTime(double distance); } // 具体策略:步行 (Walk) class WalkStrategy implements TravelStrategy { @Override public void travel() { System.out.println("Choose to travel on foot."); } @Override public double calculateCost(double distance) { // 步行通常没有成本 return0.0; } @Override public double calculateTime(double distance) { // 假设步行速度为每小时 5 公里 return distance / 5.0; } } // 具体策略:自行车 (Bike) class BikeStrategy implements TravelStrategy { @Override public void travel() { // 选择骑自行车出行 System.out.println("Choose to travel by bike."); } @Override public double calculateCost(double distance) { // 自行车通常没有成本 return0.0; } @Override public double calculateTime(double distance) { // 假设自行车速度为每小时 15 公里 return distance / 15.0; } } // 具体策略:汽车 (Car) class CarStrategy implements TravelStrategy { @Override public void travel() { System.out.println("Choose to travel by car."); } @Override public double calculateCost(double distance) { // 假设汽车每公里成本为 0.5 美元 return distance * 0.5; } @Override public double calculateTime(double distance) { // 假设汽车速度为每小时 60 公里 return distance / 60.0; } } // 具体策略:飞机 (Airplane) class AirplaneStrategy implements TravelStrategy { @Override public void travel() { System.out.println("Choose to travel by airplane."); } @Override public double calculateCost(double distance) { // 假设飞机每公里成本为 0.8 美元 return distance * 0.8; } @Override public double calculateTime(double distance) { // 假设飞机速度为每小时 800 公里 return distance / 800.0; } } // 上下文类:旅行规划者 (Travel Planner) class TravelPlanner { private TravelStrategy travelStrategy; public TravelPlanner(TravelStrategy travelStrategy) { this.travelStrategy = travelStrategy; } public void setTravelStrategy(TravelStrategy travelStrategy) { this.travelStrategy = travelStrategy; } public void planTravel() { travelStrategy.travel(); } public double calculateCost(double distance) { return travelStrategy.calculateCost(distance); } public double calculateTime(double distance) { return travelStrategy.calculateTime(distance); } } // 测试类 publicclass StrategyPatternExample { public static void main(String[] args) { double distance = 100.0; // 假设旅行距离为 100 公里 // 步行出行 TravelPlanner travelPlanner1 = new TravelPlanner(new WalkStrategy()); travelPlanner1.planTravel(); System.out.println("Cost: " + travelPlanner1.calculateCost(distance) + " dollars"); System.out.println("Time: " + travelPlanner1.calculateTime(distance) + " hours"); // 骑自行车出行 TravelPlanner travelPlanner2 = new TravelPlanner(new BikeStrategy()); travelPlanner2.planTravel(); System.out.println("Cost: " + travelPlanner2.calculateCost(distance) + " dollars"); System.out.println("Time: " + travelPlanner2.calculateTime(distance) + " hours"); // 开车出行 TravelPlanner travelPlanner3 = new TravelPlanner(new CarStrategy()); travelPlanner3.planTravel(); System.out.println("Cost: " + travelPlanner3.calculateCost(distance) + " dollars"); System.out.println("Time: " + travelPlanner3.calculateTime(distance) + " hours"); // 乘飞机出行 TravelPlanner travelPlanner4 = new TravelPlanner(new AirplaneStrategy()); travelPlanner4.planTravel(); System.out.println("Cost: " + travelPlanner4.calculateCost(distance) + " dollars"); System.out.println("Time: " + travelPlanner4.calculateTime(distance) + " hours"); } }代码解释:
首先,通过实现
TravelStrategy接口定义了不同的出行策略。然后,创建
TravelPlanner对象,并根据需要选择不同的出行策略。最后,调用
planTravel()方法执行出行操作,并使用calculateCost()和calculateTime()方法计算成本和时间。
优点:
易于扩展:如果你想添加新的出行方式,只需创建一个新的
TravelStrategy实现类,无需修改TravelPlanner类的代码。灵活切换:你可以轻松地在不同出行策略之间切换,以满足不同的出行需求。
代码清晰:不同出行方式的逻辑被封装在各自的策略类中,使得代码更具可读性和可维护性。
这个例子可以帮助你更好地理解策略模式在实际场景中的应用。通过不同的策略实现不同的行为,并能根据用户需求灵活选择和切换策略,同时保持了代码的可扩展性和可维护性。
此外,策略模式通常与工厂模式结合使用,可以将策略对象的创建和使用分离,使代码更加灵活和易于维护。
JDK 中的应用:
java.util.Comparator是一个典型的策略模式。
Spring 中的应用:
事务管理 (
TransactionManager),支持编程式和声明式事务。
5. 观察者模式
观察者模式定义了一种一对多的依赖关系。当一个对象的状态发生变化时,所有依赖于它的对象都会收到通知。
代码实现:股价更新系统
模拟当股价更新时通知投资者。
import java.util.ArrayList; import java.util.List; // 观察者接口 interface StockObserver { // 当股价更新时,此方法将被主题调用 void update(double price); } // 具体观察者:投资者 (Investor) class Investor implements StockObserver { private String name; public Investor(String name) { this.name = name; } @Override public void update(double price) { // 打印投资者的名字和接收到的股价更新信息 System.out.println(name + " received an update: The current stock price is " + price); } } // 主题接口 (Subject interface) interface StockSubject { // 注册观察者 void registerObserver(StockObserver observer); // 移除观察者 void removeObserver(StockObserver observer); // 通知所有注册的观察者 void notifyObservers(); } // 具体主题:股票 (Stock) class Stock implements StockSubject { private List<StockObserver> observers = new ArrayList<>(); privatedouble price; @Override public void registerObserver(StockObserver observer) { // 将观察者添加到观察者列表 observers.add(observer); } @Override public void removeObserver(StockObserver observer) { // 从观察者列表中移除观察者 observers.remove(observer); } @Override public void notifyObservers() { // 遍历观察者列表,调用每个观察者的 update 方法进行通知 for (StockObserver observer : observers) { observer.update(price); } } public void setPrice(double price) { // 设置新的股价并通知观察者 this.price = price; notifyObservers(); } } // 测试类 publicclass ObserverPatternExample { public static void main(String[] args) { Stock stock = new Stock(); Investor investor1 = new Investor("Alice"); Investor investor2 = new Investor("Bob"); Investor investor3 = new Investor("Charlie"); // 注册投资者为观察者 stock.registerObserver(investor1); stock.registerObserver(investor2); stock.registerObserver(investor3); // 更新股价,观察者将收到通知 stock.setPrice(100.0); stock.setPrice(105.0); // 移除一个观察者 stock.removeObserver(investor2); // 再次更新股价,剩余的观察者将收到通知 stock.setPrice(110.0); } }输出:
Alice received an update: The current stock price is 100.0 Bob received an update: The current stock price is 100.0 Charlie received an update: The current stock price is 100.0 Alice received an update: The current stock price is 105.0 Bob received an update: The current stock price is 105.0 Charlie received an update: The current stock price is 105.0 Alice received an update: The current stock price is 110.0 Charlie received an update: The current stock price is 110.0代码解释:
首先,创建一个
Stock对象作为主题。创建多个
Investor对象作为观察者。使用
registerObserver方法将这些投资者注册到Stock对象。当调用
setPrice方法更新股价时,所有注册的投资者会自动收到通知。你可以使用
removeObserver方法移除不需要接收通知的投资者。
JDK 中的应用:
java.util.Observer和java.util.Observablejavax.swing.event.ChangeListener
Spring 中的应用:
ApplicationEvent和ApplicationListener是典型的实现。
6. 代理模式
代理模式通过一个代理对象控制对目标对象的访问,常用于访问控制和日志记录等场景。此外,代理模式还包括静态代理和动态代理。
代码实现:静态代理
// 定义服务接口 interface Service { void execute(); } // 实现服务接口的真实服务类 class RealService implements Service { @Override public void execute() { System.out.println("RealService is executing..."); } } // 代理服务类,持有真实服务对象的引用 class ServiceProxy implements Service { private Service realService; public ServiceProxy() { this.realService = new RealService(); } @Override public void execute() { System.out.println("Additional operations before calling the real service, such as logging or permission checking"); realService.execute(); System.out.println("Additional operations after calling the real service, such as performance statistics"); } } // 测试静态代理 publicclass StaticProxyExample { public static void main(String[] args) { Service service = new ServiceProxy(); // 通过代理调用 execute 方法 service.execute(); } }代码解释:
Service接口:定义了服务的抽象方法execute。RealService类:它是实现Service接口的具体类,提供了服务的具体实现。ServiceProxy类:它是RealService的代理类,也实现了Service接口。在execute方法中,除了调用真实服务的execute方法外,还添加了前后额外的操作,实现了代理功能。在
StaticProxyExample的main方法中,创建一个ServiceProxy对象并调用execute方法,实现了对RealService的代理调用。
代码实现:动态代理
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 定义服务接口 interface DynamicService { void performTask(); } // 实现服务接口的真实服务类 class RealDynamicService implements DynamicService { @Override public void performTask() { System.out.println("RealDynamicService is performing a task..."); } } // 动态代理调用处理器 class DynamicServiceInvocationHandler implements InvocationHandler { private Object target; public DynamicServiceInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Additional operations before calling the real service, such as logging or permission checking."); Object result = method.invoke(target, args); System.out.println("Additional operations after calling the real service, such as performance statistics."); return result; } } // 测试动态代理 publicclass DynamicProxyExample { public static void main(String[] args) { RealDynamicService realService = new RealDynamicService(); // 创建调用处理器并传入真实服务对象 DynamicServiceInvocationHandler handler = new DynamicServiceInvocationHandler(realService); // 创建动态代理对象 DynamicService proxyService = (DynamicService) Proxy.newProxyInstance( DynamicService.class.getClassLoader(), new Class<?>[]{DynamicService.class}, handler); // 通过代理调用 performTask 方法 proxyService.performTask(); } }代码解释:
DynamicService接口:定义了服务的抽象方法performTask。RealDynamicService类:它是实现DynamicService接口的具体类,提供了服务的具体实现。DynamicServiceInvocationHandler类:实现了InvocationHandler接口,当代理对象的方法被调用时,其invoke方法会被触发。在invoke方法中,在调用真实服务方法前后添加了额外操作。在
DynamicProxyExample的main方法中,首先创建RealDynamicService的实例,然后创建DynamicServiceInvocationHandler实例并传入真实服务对象。接着使用Proxy.newProxyInstance方法创建DynamicService的代理对象,并调用performTask方法,从而实现动态代理。
真实源码中的应用
在 Spring 框架中,AOP(面向切面编程)是代理模式的典型应用。Spring AOP 主要使用动态代理实现。当你使用@Aspect注解定义一个切面,并使用@Before、@After、@Around等注解定义通知时,Spring 会为受切面影响的 Bean 创建代理对象。
例如,你可能有一个服务类如下:
import org.springframework.stereotype.Service; @Service public class UserService { public void addUser(String username) { System.out.println("Adding user: " + username); } }你可以定义一个切面类如下:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class UserServiceAspect { @Before("execution(* com.example.service.UserService.addUser(..))") public void beforeAddUser() { System.out.println("Before adding user..."); } }代码解释:
UserService是一个普通的服务类,包含addUser方法。UserServiceAspect是一个切面类,使用@Before注解定义了一个前置通知。当UserService的addUser方法被调用时,Spring 会为UserService创建一个代理对象,并在调用addUser方法之前执行beforeAddUser方法。
Spring 使用动态代理机制(如果是接口则使用 JDK 动态代理,否则使用 CGLIB 动态代理),在不修改UserService类代码的情况下,将切面逻辑织入到UserService的方法调用中,从而实现了横切关注点的分离。
总结
静态代理在编译时确定代理类和被代理类的关系,代码简单易懂,但如果接口方法较多,为每个方法添加代理逻辑会很繁琐。
动态代理通过反射机制在运行时生成代理对象,更加灵活,可以动态添加代理逻辑。适用于需要为多个不同的类或方法添加相同或相似代理逻辑的场景,如日志记录、性能统计、权限检查等。
Spring AOP 是动态代理在实际框架中的应用,它帮助开发者更好地实现面向切面编程,将横切关注点(如日志、事务、安全等)与业务逻辑分离,提高了代码的可维护性和可扩展性。
7. 模板方法模式
模板方法模式定义了一个算法的骨架,并将具体实现留给子类。
代码实现:制作饮料
模拟制作咖啡和茶的过程。
abstract class BeverageMaker { // 模板方法,定义制作饮料的算法骨架 public final void makeBeverage() { prepareIngredients(); brew(); pourInCup(); addCondiments(); } protected abstract void prepareIngredients(); protected abstract void brew(); // 将饮料倒入杯中,有具体实现,可以被重写 protected void pourInCup() { System.out.println("Pour the beverage into a cup"); } protected abstract void addCondiments(); } // 具体子类 CoffeeMaker,用于制作咖啡 class CoffeeMaker extends BeverageMaker { @Override protected void prepareIngredients() { System.out.println("Prepare coffee beans and water"); } @Override protected void brew() { System.out.println("Brew coffee with a coffee machine"); } @Override protected void addCondiments() { System.out.println("Add sugar and milk"); } } // 具体子类 TeaMaker,用于制作茶 class TeaMaker extends BeverageMaker { @Override protected void prepareIngredients() { System.out.println("Prepare tea leaves and hot water"); } @Override protected void brew() { System.out.println("Brew tea with hot water"); } @Override protected void addCondiments() { System.out.println("Add lemon slices"); } } // 测试类 publicclass TemplateMethodPatternDemo { public static void main(String[] args) { BeverageMaker coffeeMaker = new CoffeeMaker(); System.out.println("Making coffee:"); coffeeMaker.makeBeverage(); System.out.println("\n-----------------"); BeverageMaker teaMaker = new TeaMaker(); System.out.println("Making tea:"); teaMaker.makeBeverage(); } }代码解释:
BeverageMaker是一个抽象类,其中makeBeverage是一个模板方法。它定义了制作饮料的算法骨架,即先准备原料,然后冲泡,接着将饮料倒入杯中,最后添加调料。该方法是final的,不允许子类修改这个流程的顺序。prepareIngredients、brew和addCondiments是抽象方法。因为不同的饮料准备原料、冲泡和添加调料的方式不同,所以这些方法由具体的子类来实现。pourInCup方法是一个有默认实现的方法。对于大多数饮料来说,将饮料倒入杯中的操作是相似的,但如果子类有特殊需求,也可以重写该方法。CoffeeMaker和TeaMaker是BeverageMaker的具体子类。它们分别重写了prepareIngredients、brew和addCondiments方法,实现了制作咖啡和茶的具体步骤。在
TemplateMethodPatternDemo的main方法中,我们创建了CoffeeMaker和TeaMaker的实例,并调用它们的makeBeverage方法。这样,我们可以看到,在不改变整体制作流程的情况下,不同的饮料可以按照自己的特点完成各个步骤。
这个例子更贴近实际生活。它通过制作饮料的过程展示了模板方法模式如何分离算法的框架和具体实现,提高了代码的复用性和可维护性。对于不同的饮料,我们只需要关注它们各自的特点,而不需要重复实现整个制作流程,体现了模板方法模式的优势。
通过使用模板方法模式,我们可以在保证流程一致性的同时,根据具体情况灵活定制不同的步骤,使得代码更加清晰、易于扩展和维护。
JDK 中的应用:
java.util.AbstractList和java.util.AbstractMap
Spring 中的应用:
JdbcTemplate和RestTemplate