如何优雅的将设计模式运用到实际项目中去?

场景: 商场搞活动,根据客户购买商品的金额,收费时给与不同的打折,比如,购买 金额>=2000 的打八折(0.8),金额 500 ~ 1000 的,打九折(0.9),购买金额 0 ~ 500 的九五折(0.95),根据不同的金额走不同计算策略逻辑。

3.1 策略模式

首先定义一个Strategy接口来表示一个策略:

scss 复制代码
public interfaceStrategy{

/**
     * 采用策略
     */
Stringstrategy();

/**
     * 计算方法逻辑
     */
voidalgorithm();
}

其中strategy方法返回当前策略的唯一标识,algorithm则是该策略的具体执行的计算逻辑。

下面是Strategy接口的两个实现类:

typescript 复制代码
public classConcreteStrategyAimplementsStrategy{

@Override
publicStringstrategy(){
returnStrategySelector.strategyA.getStrategy();
}

@Override
publicvoidalgorithm(){
System.out.println("process with strategyA...");
}
}

publicclassConcreteStrategyBimplementsStrategy{

@Override
publicStringstrategy(){
returnStrategySelector.strategyB.getStrategy();
}

@Override
publicvoidalgorithm(){
System.out.println("process with strategyB...");
}
}

publicclassConcreteStrategyCimplementsStrategy{

@Override
publicStringstrategy(){
returnStrategySelector.strategyC.getStrategy();
}

@Override
publicvoidalgorithm(){
System.out.println("process with strategyC...");
}
}

自定义策略选择枚举 StrategySelector:

scss 复制代码
@Getter
publicenumStrategySelector{

    strategyA(1,"strategyA"),
    strategyB(2,"strategyB"),
    strategyC(3,"strategyC");

privateInteger code;
privateString strategy;

StrategySelector(Integer code,String strategy){
this.code = code;
this.strategy = strategy;
}
}

然后定义一个StrategyRunner接口用来表示策略的调度器:

arduino 复制代码
public interface StrategyRunner {
    void execute(String strategy);
}

execute方法内部通过判断strategy的值来决定具体执行哪一个策略。

scss 复制代码
public classStrategyRunnerImplimplementsStrategyRunner{

privatestaticfinalList<Strategy> STRATEGIES =Arrays.asList(newConcreteStrategyA(),newConcreteStrategyB(),newConcreteStrategyC());
privatestaticMap<String,Strategy> STRATEGY_MAP =Maps.newHashMap();

static{
        STRATEGY_MAP = STRATEGIES.stream().collect(Collectors.toMap(Strategy::strategy, s -> s));
}

@Override
publicvoidexecute(String strategy){
        STRATEGY_MAP.get(strategy).algorithm();
}
}

在StrategyRunnerImpl内部,定义了一个STRATEGIES列表来保存所有Strategy实现类的实例,以及一个叫做STRATEGY_MAP的Map来保存strategy和Strategy实例之间的对应关系,static块中的代码用于从STRATEGIES列表构造STRATEGY_MAP

这样,在execute方法中就可以很方便地获取到指定strategy的Strategy实例。

实现并运用策略模式

scss 复制代码
@Component
publicclassConcreteStrategyAimplementsStrategy{

@Override
publicStringstrategy(){
returnStrategySelector.strategyA.getStrategy();
}

@Override
publicvoidalgorithm(){
System.out.println("process with strategyA...");
}
}

@Component
publicclassConcreteStrategyBimplementsStrategy{

@Override
publicStringstrategy(){
returnStrategySelector.strategyB.getStrategy();
}

@Override
publicvoidalgorithm(){
System.out.println("process with strategyB...");
}
}

@Component
publicclassConcreteStrategyCimplementsStrategy{

@Override
publicStringstrategy(){
returnStrategySelector.strategyC.getStrategy();
}

@Override
publicvoidalgorithm(){
System.out.println("process with strategyC...");
}
}

然后,定义一个StrategyConfig配置类,用于向容器注入一个StrategyRunner:

scss 复制代码
@Configuration
publicclassStrategyConfig{

@Bean
publicStrategyRunnerrunner(List<Strategy> strategies){
Map<String,Strategy> strategyMap = strategies.stream().collect(Collectors.toMap(Strategy::strategy, s -> s));
return flag -> strategyMap.get(flag).algorithm();
}
}

不难发现,strategyRunner方法的实现,其中的逻辑与之前的StrategyRunnerImpl几乎完全相同,也是根据一个List<Strategy>来构造一个Map<String, Strategy>

只不过,这里的strategies列表不是我们自己构造的,而是通过方法参数传进来的。

由于strategyRunner标注了Bean注解,因此参数上的List<Strategy>实际上是在Spring Boot初始化过程中从容器获取的,所以我们之前向容器中注册的那两个实现类会在这里被注入。

这样,我们再也无需操心系统中一共有多少个Strategy实现类,因为Spring Boot的自动配置会帮我们自动发现所有实现类。我们只需编写自己的Strategy实现类,然后将它注册进容器,并在任何需要的地方注入StrategyRunner:

java 复制代码
@Autowired private StrategyRunner strategyRunner;

然后直接使用strategyRunner就行了:

less 复制代码
@RestController
@RequestMapping(value = "/designPatterns")
publicclassDesignPatternController{

@Autowired
privateStrategyRunner strategyRunner;

@GetMapping(value = "/algorithm")
publicvoidalgorithm(@RequestParam("strategy") String strategy){
        strategyRunner.execute(strategy);
}
}

访问接口,控制台输出如下:

arduino 复制代码
process with strategyA...

3.2 简单工厂模式

举个场景例子🌰:

用户支付场景,目前支持支付宝支付和微信支付,未来会新增银行卡,云闪付等方式。使用策略模式,每一种支付方式都是一种策略,根据用户传入的支付类型,创建不同的策略类,使用工厂模式,通过封装一个PaymentStrategyHandler策略处理类,其他系统直接通过一个统一的入口,进行该功能的调用,使用门面模式。

3.2.1 定义一个策略类:

typescript 复制代码
public interfaceIPayment{

/**
     * 支付
     *
     * @param paymentBody
     */
Booleanpay(PaymentBody paymentBody);
}

publicclassAliPayimplementsIPayment{
@Override
publicBooleanpay(PaymentBody paymentBody){
System.out.println("支付宝支付...");
returnBoolean.TRUE;
}
}

publicclassWechatPayimplementsIPayment{
@Override
publicBooleanpay(PaymentBody paymentBody){
System.out.println("微信支付...");
returnBoolean.TRUE;
}
}

publicclassUnionPayimplementsIPayment{
@Override
publicBooleanpay(PaymentBody paymentBody){
System.out.println("银联支付...");
returnBoolean.TRUE;
}
}

3.2.2 创建策略工厂

typescript 复制代码
package com.universal.core.designPatterns.factory;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ReflectUtil;
import com.universal.core.designPatterns.enums.PayStrategyEnum;
import org.springframework.stereotype.Component;

/**
 * Factory for payment methods
 */
@Component
publicclassPaymentFactory{

publicstaticIPaymentgetPayStrategy(String type){
// 1.通过枚举中的type获取对应的value
Stringvalue=EnumUtil.getFieldBy(PayStrategyEnum::getValue,PayStrategyEnum::getType, type);
// 2.使用反射机制创建对应的策略类
IPaymentpayment=ReflectUtil.newInstance(value);
return payment;
}
}

3.3.3 定义策略枚举

typescript 复制代码
/**
 * 支付策略枚举
 */
@Getter
publicenumPayStrategyEnum{

    ZFB("ZFB","com.universal.core.designPatterns.factory.impl.AliPay"),
    WX("WX","com.universal.core.designPatterns.factory.impl.WechatPay"),
    UNION("UNION","com.universal.core.designPatterns.factory.impl.UnionPay");
String type;
String value;

PayStrategyEnum(String type,String value){
this.type = type;
this.value = value;
}
}

3.3.4 创建策略的上下文角色

scss 复制代码
@Data
publicclassPaymentContext{

@Resource
privateIPayment paymentStrategy;

publicPaymentContext(IPayment paymentStrategy){
this.paymentStrategy = paymentStrategy;
}

publicBooleanpay(PaymentBody paymentBody){
returnthis.paymentStrategy.pay(paymentBody);
}
}

3.4.5 提供统一访问处理入口

scss 复制代码
package com.universal.core.designPatterns.factory;

import cn.hutool.core.util.EnumUtil;
import com.universal.core.designPatterns.enums.PayStrategyEnum;
import org.springframework.stereotype.Component;

@Component
publicclassPaymentStrategyHandler{

publicstaticBooleanpay(PaymentBody payBody){
if(!EnumUtil.contains(PayStrategyEnum.class, payBody.getType())){
thrownewIllegalArgumentException("不支持的支付方式!");
}
// 1.获取支付策略对象
IPaymentpayStrategy=PaymentFactory.getPayStrategy(payBody.getType());
// 2.获取支付策略上下文
PaymentContextpaymentContext=newPaymentContext(payStrategy);
// 3.进行支付
return paymentContext.pay(payBody);
}
}

3.4.6 创建Controller

less 复制代码
@RestController
@RequestMapping(value = "/designPatterns")
publicclassDesignPatternController{

@PostMapping("/pay")
publicBooleanpay(@RequestBody PaymentBody paymentBody){
returnPaymentStrategyHandler.pay(paymentBody);
}

}

3.3 单例模式

代码演示:

scss 复制代码
//懒汉式(静态内部类)
classSingleton{
privateSingleton(){}

//写一个静态内部类,该类中有一个静态属性Singleton
privatestaticclassSingletonInstance{
privatestaticfinalSingletonINSTANCE=newSingleton();
}

publicstaticsynchronizedSingletongetInstance(){
returnSingletonInstance.INSTANCE;
}
}

3.4 代理模式

代理模式Proxy, 为其他对象提供一种代理以控制对这个对象的访问。

代理模式 实际上在平时中也运用的非常广泛,最经典的例子就是房东委托中介代理出租房子的案例,本文也是采用这个案例对代理模式进行解释和代码实现。

代码实例🌰:

创建一个Subject类:

csharp 复制代码
/**
 * 活动类,目的是出租房子
 */
public interface Subject {

    /**
     * 租房接口
     */
    void rentHouse();
}

定义一个房东角色,现在活动类:

less 复制代码
/**
 * 房东
 */
publicclassHouseOwnerimplementsSubject{

/**
     * 实现租房方法
     */
@Override
publicvoidrentHouse(){
System.out.println("房东成功出租了房子...");
}
}

定义一个中介代理对象:

scss 复制代码
/**
 * 中介代理类
 *
 * 一般情况下我们不能直接联系到房东,所以需要提供一个代理类,即中介类
 */
publicclassHouseProxyimplementsSubject{

privateHouseOwnerhouseOwner=newHouseOwner();

@Override
publicvoidrentHouse(){
System.out.println("中介收取代理费,帮助房东出租房子...");
        houseOwner.rentHouse();
}
}

模拟用户找中介租房子:

3.5 工厂方法模式

上面我们也提到了简单工厂模式,那么工厂方法模式和简单工厂的区别在于哪里呢,其实,简单工厂模式的最大优点在于包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,相对于客户端来说,去除了与具体产品的依赖。

工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法是一个类的实例化延迟到其子类,通俗来说:它提供了一种实例化逻辑委托子类的方法。

代码示例:

定义NetworkConfigFactoryService工厂类

java 复制代码
package com.universal.core.designPatterns.factoryMethod.factory;

import com.universal.core.designPatterns.factoryMethod.NetworkConfigCrudService;

public interface NetworkConfigFactoryService {

    /**
     * 获取指定的处理逻辑类
     * 
     * @param productType
     * @return
     */
    NetworkConfigCrudService getSpecificService(String productType);
}

NetworkConfigFactoryService工厂实现类

ini 复制代码
@Service
publicclassNetworkConfigFactoryServiceImplimplementsNetworkConfigFactoryService{

privatefinalAServiceImpl aService;
privatefinalBServiceImpl bService;
privatefinalCServiceImpl cService;
privatefinalDServiceImpl dService;

@Override
publicNetworkConfigCrudServicegetSpecificService(String productType){
NetworkConfigCrudServicenetworkConfigCrudService=null;
switch(productType){
case"A":
                networkConfigCrudService = aService;
break;
case"B":
                networkConfigCrudService = bService;
break;
case"C":
                networkConfigCrudService = cService;
break;
case"D":
                networkConfigCrudService = dService;
break;
}
return networkConfigCrudService;
}
}

定义网点操作接口NetworkConfigCrudService

csharp 复制代码
public interface NetworkConfigCrudService {

    NetworkConfigVO getNetwork(NetworkConfigDTO networkConfigDTO);
}

它的实现类分别是 AServiceImplBServiceImplCServiceImplDServiceImpl,分别对应不同的逻辑:

scss 复制代码
@Service
publicclassAServiceImplimplementsNetworkConfigCrudService{
@Override
publicNetworkConfigVOgetNetwork(NetworkConfigDTO networkConfigDTO){
returnnewNetworkConfigVO();
}
}

@Service
publicclassBServiceImplimplementsNetworkConfigCrudService{
@Override
publicNetworkConfigVOgetNetwork(NetworkConfigDTO networkConfigDTO){
returnnewNetworkConfigVO();
}
}
@Service
publicclassCServiceImplimplementsNetworkConfigCrudService{
@Override
publicNetworkConfigVOgetNetwork(NetworkConfigDTO networkConfigDTO){
returnnewNetworkConfigVO();
}
}
@Service
publicclassDServiceImplimplementsNetworkConfigCrudService{
@Override
publicNetworkConfigVOgetNetwork(NetworkConfigDTO networkConfigDTO){
returnnewNetworkConfigVO();
}
}

控制层NetworkConfigController

less 复制代码
@RestController
@Slf4j
@RequestMapping(value = "/networkConfig")
publicclassNetworkConfigController{
privatefinalNetworkConfigFactoryService factoryService;

@PostMapping(value = "/getNetworkDetails", produces = MediaType.APPLICATION_JSON_VALUE)
publicApiResult<NetworkConfigVO>getNetworkDetails(@RequestBody NetworkConfigDTO networkConfigDTO){
//获取AService处理类逻辑
NetworkConfigCrudServiceaService= factoryService.getSpecificService("A");
NetworkConfigVOnetwork= aService.getNetwork(networkConfigDTO);
returnApiResult.success(network);
}
}

3.6 观察者模式

观察者模式Observer 定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

初识观察者模式:报社+订阅者 = 观察者模式。

要点

  • • 观察者模式定义了对象之间一对多的关系。
  • • 主题(可观察者)用一个共同的接口来更新观察者。
  • • 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
  • • 使用次模式时,你可以从被观察者处推或拉数据(推的方式被认为是更正确的)。
  • • 有多个观察者时,不可以依赖特定的通知次序。
  • • java中有多种观察者模式的实现,包括了通用的java.util.Observable,不过需要注意Observable实现上所带来的问题,有必要的话,可以实现自己的Observable。
  • • Spring也大量使用观察者模,比如ListenrEvent消息订阅与发布;

案例

直接以气象站为例,其中天气信息就表示被观察者,天气布告板就表示订阅者和观察者,当天气发生变化(被观察者)时,会通过notifyObserver通知所有观察者,并调用他们的控制方法处理数据。

一个WeatherData对象负责追踪目前的天气状况(温度,湿度,气压)。我们希望你们能建立一个应用,有三种布告板,分别显示目前的状况、气象统计及简单的预报。当WeatherObject对象获得最新的测量数据时,三种布告板必须实时更新。

气象监测应用的对象分析

此系统中的三个部分是:

  • • 气象站(获取实际气象数据的物理装置)
  • • WeatherData对象(最总来自气象站的数据,并更新布告板)
  • • 布告板(显示目前天气状况给用户看)。

实现气象站

scss 复制代码
//主题接口
interfaceSubject{
//注册观察者
publicvoidregisterObserver(Observer o);
//删除观察者
publicvoidremoveObserver(Observer o);
//当主题状态改变时,这个方法会被调用,以通知所有的观察者
publicvoidnotifyObserver();
}

interfaceObserver{
//当气象观测值改变时,主题会把这些状态值当作方法的参数,传送给观察者
publicvoidupdate(float temp,float humidity,float pressure);
}

interfaceDisplay{
//当布告板需要显示时,调用此方法
publicvoiddisplay();
}

在WeatherData中实现主题接口

scss 复制代码
class WeatherDataimplementsSubject{

privateArrayList<Observer> observers;
privatefloat temperature;
privatefloat humidity;
privatefloat pressure;

publicWeatherData(){
        observers=newArrayList<Observer>();
}

@Override
publicvoidregisterObserver(Observer o){
        observers.add(o);
}

@Override
publicvoidremoveObserver(Observer o){
int i=observers.indexOf(o);
if(i>=0){
            observers.remove(i);
}
}

@Override
publicvoidnotifyObserver(){
for(Observer observer:observers){
            observer.update(temperature,humidity,pressure);
}
}
//当从气象站得到更新观测值时,我们通知观察者
publicvoidmeasurementsChanged(){
        notifyObserver();
}

publicvoidsetMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
        measurementsChanged();
}
//WeatherData的其他方法
}

建立布告板

其中的一个布告板:

kotlin 复制代码
class CurrentConditionDisplayimplementsObserver,DisplayElement{
// 温度
privatefloat temperature;
// 湿度
privatefloat humidity;
// 气压
privatefloat pressure;

privateSubject weatherData;

publicCurrentConditionDisplay(Subject weatherData){
this.weatherData=weatherData;
        weatherData.registerObserver(this);
}
@Override
publicvoiddisplay(){
System.out.println("这里气象台更新的天气数据...");
}

@Override
publicvoidupdate(float temp, float humidity, float pressure){
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure
display();
}
}

利用内置的支持重写WeatherData

javascript 复制代码
class WeatherDataTWOextendsObservable{
privatefloat temperature;
privatefloat humidity;
privatefloat pressure;

publicWeatherDataTWO(){

}

publicvoidmeasurementsChanged(){
//在调用notifyObservers()之前,要先调用setChanged()来指示状态已经改变
        setChanged();
//我们没有调用notifyObservers传送数据对象,表示我们采用的做法是拉。
        notifyObservers();
}

publicvoidsetMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
        measurementsChanged();
}

publicfloatgetTemperature(){
return temperature;
}

publicfloatgetHumidity(){
return humidity;
}

publicfloatgetPressure(){
return pressure;
}
}

利用内置观察者重写布告板

ini 复制代码
class CurrentConditionsDisplayimplementsjava.util.Observer,DisplayElement{

Observable observable;
privatefloat temperature;
privatefloat humidity;

publicCurrentConditionsDisplay(Observable observable){
this.observable=observable;
        observable.addObserver(this);
}

@Override
publicvoiddisplay(){
System.out.println("这里气象台更新的天气数据...");
}

@Override
publicvoidupdate(Observable o, Object arg){
if(o instanceofWeatherDataTWO){
WeatherDataTWO weatherDataTWO=(WeatherDataTWO) o;
this.temperature=weatherDataTWO.getTemperature();
this.humidity=weatherDataTWO.getHumidity();
            display();
}
}
}

3.7 模板方法模式

模板方法(Template Method) 是一种行为设计模式。模板方法设计模式用于创建方法存根并将某些实现步骤推迟到子类。

模板方法定义了执行算法的步骤,它可以提供可能对所有或者部分子类通用的默认实现,下面通过一个简单的例子来理解这个模式,假设我们想提供一种算法了该房子,建造房屋需要执行的步骤是:建造地基->建造支柱->建造墙壁和窗户。

重点的一点是我们不能改变执行的顺序,因为我们不能在构建基础之前构建窗口,所以在这种情况下,我们可以创建一个模板方法,它将使用不同的方法来建造房子,现在盖房子的地基对于所有类型的房子都是一样的,无论是木房、玻璃房子还是混泥土房。

所以我们可以为此提供基础实现,如果子类想要覆盖这个方法,他们可以自己选择,但是大多数情况下,所有类型的房屋都很常见。为了确保子类不覆盖模板方法,我们应该将其设为最终方法。

模板方法抽象类

由于我们希望某些方法由子类实现,因此我们必须将我们的基类设为抽象类。

定义抽象类HouseTemplate

scss 复制代码
public abstractclassHouseTemplate{

/**
     * buildHouse()是模板方法,定义个执行几个步骤的执行顺序
     *
     * template method, final so subclasses can't override final修饰,子类不能重写
     */
publicfinalvoidbuildHouse(){
//建造地基
        buildFoundation();
//建造柱子
        buildPillars();
//建造墙壁
        buildWalls();
//建造窗户
        buildWindows();
System.out.println("House is built successfully");
}

privatevoidbuildFoundation(){
System.out.println("Building foundation with cement, iron rods and sand");
}

/**
     * methods to be implemented by subclasses
     */
publicabstractvoidbuildPillars();
publicabstractvoidbuildWalls();

/**
     * default implementation
     */
privatevoidbuildWindows(){
System.out.println("Building Glass Windows");
}
}

WoodenHouse

typescript 复制代码
package com.universal.core.designPatterns.templateMethod;

/**
 * 木房
 */
publicclassWoodenHouseextendsHouseTemplate{
@Override
publicvoidbuildPillars(){
System.out.println("Building Pillars With Wood coating...");
}

@Override
publicvoidbuildWalls(){
System.out.println("Building Wooden Walls...");
}
}

GlassHouse

typescript 复制代码
package com.universal.core.designPatterns.templateMethod;

/**
 * 玻璃房
 */
publicclassGlassHouseextendsHouseTemplate{

@Override
publicvoidbuildPillars(){
System.out.println("Building Pillars With Glass coating...");
}

@Override
publicvoidbuildWalls(){
System.out.println("Building Glass Walls...");

}
}

ConcreteHouse

typescript 复制代码
package com.universal.core.designPatterns.templateMethod;

/**
 * 混泥土房屋
 */
publicclassConcreteHouseextendsHouseTemplate{
@Override
publicvoidbuildPillars(){
System.out.println("Building Pillars With Concrete coating...");
}

@Override
publicvoidbuildWalls(){
System.out.println("Building Concrete Walls...");

}
}

HousingClient

ini 复制代码
package com.universal.core.designPatterns.templateMethod;

publicclassHousingClient{

publicstaticvoidmain(String[] args){
HouseTemplatehouseBuilder=newWoodenHouse();
        houseBuilder.buildHouse();

System.out.println("--------------");

        houseBuilder =newGlassHouse();
        houseBuilder.buildHouse();

System.out.println("--------------");
        houseBuilder =newConcreteHouse();
        houseBuilder.buildHouse();

}
}

输出结果:

markdown 复制代码
Building foundation with cement,iron rods and sand
BuildingPillarsWithWood coating...
BuildingWoodenWalls...
BuildingGlassWindows
Houseis built successfully
--------------
Building foundation with cement,iron rods and sand
BuildingPillarsWithGlass coating...
BuildingGlassWalls...
BuildingGlassWindows
Houseis built successfully
--------------
Building foundation with cement,iron rods and sand
BuildingPillarsWithConcrete coating...
BuildingConcreteWalls...
BuildingGlassWindows
Houseis built successfully

Process finished withexit code 0

3.8 适配器模式

适配器模式Adapter 是将一个接口转换成另一个客户所期望的接口。Adapter 适配器让那些本来因为接口不兼容的类可以合作无间。

适配器模式中的角色分析

  • • 目标接口(Traget):客户期待的接口,目标可以是具体的或者抽象的类,也可以是接口。
  • • 需要适配的对象(Source Adaptee):需要适配的对象。
  • • 适配器(Adapter):通过包装一个需要适配的对象,把原接口转成目标接口。

举个例子🌰:我们以网线上网为例,现在有一根水晶头网线,但是它的接口与电脑的不匹配(因为电脑的是usb或者typec),那么就需要一个转接头,也就是我们说的适配器,才能够上网,下面的转接头可以理解为适配器:

类适配器

首先我们拥有一根网线, 他有上网的功能,但是它的接口与电脑不匹配:

csharp 复制代码
//要适配的类:网线
public class Adaptee {
    //功能:上网
    public void request(){
        System.out.println("链接网线上网");
    }
}

因此我们定义了一个usb接口,也就是上面提到的目标接口(Target):

csharp 复制代码
//接口转换器的抽象实现
public interface NetToUsb {
    
    //作用:处理请求,网线 => usb
    public void handleRequest();
}

定义一个适配器继承着网线,连接着usb接口:

scss 复制代码
//真正的适配器,余姚链接usb,连接网线
publicclassAdapterextendsAdapteeimplementsNetToUsb{

@Override
publicvoidhandleRequest(){
//可以上网了
super.request();
}
}

上网的具体实现:

scss 复制代码
//客户端类:想上网,插不上网线
publicclassComputer{
//电脑需要连接上转接器才可以上网
publicvoidnet(NetToUsb adapter){
//上网的具体实现:找一个转接头
        adapter.handleRequest();
}

publicstaticvoidmain(String[] args){
//电脑,适配器,网线
Computercomputer=newComputer();//电脑
Adapteradapter=newAdapter();//转接器
        computer.net(adapter);//电脑直接连接转接器就可以
}
}

对象适配器应用

  • java.util.Arrays#asList()open in new window
  • java.util.Collections#list()open in new window
  • java.util.Collections#enumeration()open in new window
  • javax.xml.bind.annotation.adapters.XMLAdapter

四、总结

设计模式(Design pattern) 代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

相关推荐
追逐时光者29 分钟前
.NET 在 Visual Studio 中的高效编程技巧集
后端·.net·visual studio
大梦百万秋1 小时前
Spring Boot实战:构建一个简单的RESTful API
spring boot·后端·restful
斌斌_____1 小时前
Spring Boot 配置文件的加载顺序
java·spring boot·后端
路在脚下@2 小时前
Spring如何处理循环依赖
java·后端·spring
海绵波波1072 小时前
flask后端开发(1):第一个Flask项目
后端·python·flask
小奏技术3 小时前
RocketMQ结合源码告诉你消息量大为啥不需要手动压缩消息
后端·消息队列
AI人H哥会Java5 小时前
【Spring】控制反转(IoC)与依赖注入(DI)—IoC容器在系统中的位置
java·开发语言·spring boot·后端·spring
凡人的AI工具箱5 小时前
每天40分玩转Django:Django表单集
开发语言·数据库·后端·python·缓存·django
奔跑草-5 小时前
【数据库】SQL应该如何针对数据倾斜问题进行优化
数据库·后端·sql·ubuntu
中國移动丶移不动5 小时前
Java 并发编程:原子类(Atomic Classes)核心技术的深度解析
java·后端