当前位置: 首页 > news >正文

设计模式-结构性模式

结构型模式

  • 1. 适配器模式(Adapter Pattern)
  • 2. 桥接模式(Bridge Pattern)
  • 3. 装饰器模式(Decorator Pattern)
    • 步骤 1:定义咖啡接口
    • 步骤 2:具体的咖啡类
    • 步骤 3:装饰器抽象类
    • 步骤 4:具体的装饰器类
    • 步骤 5:使用装饰器
  • 4. 组合模式(Composite Pattern)
    • 4.1 定义组件接口
    • 4.2 实现基本的图形
    • 4.3 实现复合图形
    • 4.4 客户端代码
  • 5. 外观模式(Facade Pattern)
  • 6. 享元模式(Flyweight Pattern)
  • 7.代理模式(Proxy Pattern)
    • 步骤 1:定义EDMO接口
    • 步骤 2:实现EDMO接口
    • 步骤 3:创建动态代理的InvocationHandler
    • 步骤 4:创建EDMO的代理对象并调用

1. 适配器模式(Adapter Pattern)

适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换成客户端所期待的另一个接口,使因接口不兼容而不能一起工作的类可以一起工作。适配器模式分为类适配器模式和对象适配器模式,这里将提供一个基于对象适配器模式的Java示例。

假设我们有一个老旧的电源插座(OldPowerSocket),它的输出电压和电流与我们新买的电器(NewElectricAppliance)不匹配。为了能让这个新电器使用,我们需要一个适配器(Adapter)来转换插座的输出。

首先,我们定义老旧的电源插座接口和它的实现:

// 老旧电源插座接口
public interface OldPowerSocket {int outputVoltage(); // 输出电压int outputCurrent(); // 输出电流
}// 老旧电源插座实现
public class OldPowerSocketImpl implements OldPowerSocket {@Overridepublic int outputVoltage() {return 220; // 假设老旧插座输出电压为220V}@Overridepublic int outputCurrent() {return 10; // 假设老旧插座输出电流为10A}
}

接下来,我们定义新电器所需要的电源接口

// 新电器电源接口
public interface NewPowerSource {int requiredVoltage(); // 所需电压int requiredCurrent();// 所需电流
}

现在,我们需要编写一个适配器,它将 OldPowerSocket 适配为 NewPowerSource:

// 适配器
public class PowerAdapter implements NewPowerSource {private OldPowerSocket oldSocket;public PowerAdapter() {this.oldSoket = oldSocket;}@Overridepublic int requiredVoltage() {// 这里只是简单的将电压转换为电器所需的电压(假设直接转换即可)// 在实际应用中,可能需要更复杂的转换逻辑return oldSocket.outputVoltage();}@Override  public int requiredCurrent() {  // 假设新电器需要的电流与老旧插座输出电流相同(这里仅为示例)  // 在实际应用中,可能需要根据实际情况进行转换  return oldSocket.outputCurrent();  }
}

最后,我们编写客户端代码来使用这个适配器:

public class Client {  public static void main(String[] args) {  // 创建老旧电源插座的实例  OldPowerSocket oldSocket = new OldPowerSocketImpl();  // 创建适配器实例,将老旧电源插座适配为新电器所需的电源  NewPowerSource newPowerSource = new PowerAdapter(oldSocket);  // 使用适配后的电源为新电器供电(这里只是演示接口调用)  System.out.println("Required Voltage: " + newPowerSource.requiredVoltage());  System.out.println("Required Current: " + newPowerSource.requiredCurrent());  }  
}

这个示例展示了如何通过适配器模式将一个类(老旧电源插座)的接口适配为另一个类(新电器)所期望的接口,从而允许它们一起工作。在实际应用中,适配器的转换逻辑可能会更加复杂,包括数据格式的转换、性能优化等。

2. 桥接模式(Bridge Pattern)

桥接模式(Bridge Pattern)是一种结构型设计模式,它旨在将抽象部分与它的实现部分分离,使它们都可以独立地变化。在桥接模式中,我们通常会有层次的结构:一个层次是抽象化角色(Abstraction),它维护对实现化角色(Implementor)的引用;另一个层次是实现化角色,它定义了实现接口的类。

以下是一个简单的Java示例,展示了如何使用桥接模式来实现绘图功能,其中图形的颜色和形状分别作为两个独立地层次进行设计和实现。

首先,我们定义实现化角色的接口 DrawAPI:

public interface DrawAPI {  void drawCircle(int radius, int x, int y);  // 圆void drawRectangle(int width, int height, int x, int y); // 正方形 
}

然后,我们实现这个接口,为不同的颜色提供具体的实现类,比如 RedCircle 和 GreenCircle(注意这里仅为示例,实际中我们可能通过颜色类来同意处理):

// 红色画笔  
public class RedCircle implements DrawAPI {  @Override  public void drawCircle(int radius, int x, int y) {  System.out.println("Drawing Circle[ color: red, radius: " + radius + ", x: " + x + ", " + y + "]");  }  @Override  public void drawRectangle(int width, int height, int x, int y) {  // 这里我们可能不需要实现所有方法,但为了演示接口完整性,我们还是声明了  System.out.println("Red does not support drawing rectangle!");  }  
}  // 绿色画笔(省略drawRectangle实现,因为它与RedCircle相同)  
public class GreenCircle implements DrawAPI {  @Override  public void drawCircle(int radius, int x, int y) {  System.out.println("Drawing Circle[ color: green, radius: " + radius + ", x: " + x + ", " + y + "]");  }  @Override  public void drawRectangle(int width, int height, int x, int y) {  System.out.println("Green does not support drawing rectangle!");  }  
}

接下来,我们定义抽象化角色 Shape,它包含一个 DrawAPI 类型的引用:

public abstract class Shape {  protected DrawAPI drawAPI;  protected Shape(DrawAPI drawAPI) {  this.drawAPI = drawAPI;  }  public abstract void draw();  
}

然后,我们创建具体的形状类,如 Circle,并在构造函数中传入 DrawAPI 的实现:

public class Circle extends Shape {  private int x, y, radius;  public Circle(int x, int y, int radius, DrawAPI drawAPI) {  super(drawAPI);  this.x = x;  this.y = y;  this.radius = radius;  }  @Override  public void draw() {  drawAPI.drawCircle(radius, x, y);  }  
}

最后,我们编写客户端代码来演示如何使用桥接模式:

public class BridgePatternDemo {  public static void main(String[] args) {  Shape redCircle = new Circle(100, 100, 10, new RedCircle());  Shape greenCircle = new Circle(100, 100, 10, new GreenCircle());  redCircle.draw();  greenCircle.draw();  }  
}

这个示例展示了如何将图形的颜色(实现化角色)和形状(抽象化角色)分离,使得它们可以独立地变化。通过桥接模式,我们可以在不修改现有代码的情况下,增加新的形状或颜色。

3. 装饰器模式(Decorator Pattern)

装饰器模式(Decorator Pattern)是一种结构型设计模式允许向一个现有的对象添加新的功能,同时又不改变其结构。就增加功能来说,装饰器模式相比生成子类更为灵活。这种模式创建了一个包装对象,也就是装饰器,来包裹真实的对象。

在Java中,装饰器模式通常通过创建一个实现了同一个接口的类的包装类(即装饰类)来实现。让我们以一个简单的咖啡订购系统为例,来展示如何使用装饰器模式。

首先,定义一个咖啡的接口,然后定义一个具体的咖啡类(Espresso),之后创建多个装饰器类(如 Milk 和 Whip)来添加不同的配料。

步骤 1:定义咖啡接口

public interface Coffee {String getDescription();double cost();
}

步骤 2:具体的咖啡类

public class Espresso implements Coffee {@Overridepublic String getDesription() {return "Espresso";}@Overridepublic double cost() {return 1.99;}
}

步骤 3:装饰器抽象类

public abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee decoratedCoffee) {this.decoratedCoffee = decoratedCoffee;}@Overridepublic String getDescription() {return decoratedCoffee.getDescrition();}@Overridepublic double cost() {return decoratedCoffee.cost();}
}

步骤 4:具体的装饰器类

// 牛奶装饰器
public class Milk extends CoffeeDecorator {public Milk(Coffee decoratedCoffee) {super(decoratedCoffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescrition() + ",Milk";}@Overridepublic double cost() {return decoratedCoffee.cost() + 0.10;}
}// 奶泡装饰器
public class Whip extends CoffeeDecorator {public Whip() {super(decoratedCoffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + ",Whip";}@Overridepublic double cost() {return decoratedCoffee.cost() + 0.15;}
}

步骤 5:使用装饰器

public class CoffeeOrder {public static void main(String[] args) {Coffee espresso = new Espresso();Coffee espressoWithMilk = new Milk(espresso);Coffee espressWithMilkAndWhip = new Whip(espressoWitnMilk);Ststem.out.println(esressoWithMilkAndWhip.getDesription() + " $" + espressoWithMilkAndWhip.cost())// 输出:Espresso,Milk,Whip $2.24}
}

在这个例子中,我们定义了一个 Espresso 咖啡,然后通过 Milk 和 whip 装饰器来增加新的特性和成本。通过组合这些装饰器,我们可以创建出各种不同的咖啡,而无需修改原始的 Espresso 类或定义大量的子类。这就是装饰器模式的强大之处。

4. 组合模式(Composite Pattern)

组合模式(Composite Pattern)允许你将对象组合成树形结构以表示“不分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

以下是一个使用Java实现的组合模式的简单Demo。在这个Demo中,我们将构架一个图形界面组件的层次结构,其中可以包含基本的图形(如矩形、圆形)和符合图形(如图形组)。

4.1 定义组件接口

首先,定义一个所有图形组件都实现的接口,包括基本的图形和符合图形。

public interface Shape {void draw(String prefix);
}

4.2 实现基本的图形

然后,实现一些基本的图形,如矩形和圆形。

// 矩形
public class Rectangle implements Shape {private String name;public Rectangle() {this.name = name;}@Overridepublic void draw(){System.out.println(prefix + "-Drawing Rectangle: " + name);}
}// 圆形
public class Circle implements Shape {private String name;public Circle(String name) {this.name = name;}@Overridepublic void draw(String prefix) {System.out.println(prefix + "-Drawing Circle: " + name);}
}

4.3 实现复合图形

接着,实现一个复合图形类,它可以包含多个子图形。

import java.util.ArrayList;
import java.util.List;public class ShapeGroup implements Shape {private List<Shape> shapes;private String name;public ShapeGroup(String name) {this.name = name;this.shapes = new ArrayList<>();}public void add(Shape shape) {shapes.add(shape);}public void remove(Shape shape) {shapes.remove(shape);}@Overridepublic void draw(String prefix) {System.out.println(prefix + "-Drawing ShapeGroup: " + name);for (Shape shape : shapes) {shape.draw(prefix + "  ");}}
}

4.4 客户端代码

最后,编写客户端代码来组合和使用这些图形。

public class CompositePatternDemo {public static void main(String[] args) {// 创建基本图形Shape circle = new Circle("Circle");Shape rectangle = new Rectangle("Rectangle");// 创建复合图形ShapeGroup group = new ShapeGroup("MyShapes");group.add(circle);group.add(rectangle);// 添加另一个复合图形ShapeGroup anotherGroup = new ShapeGroup("AnotherGroup");anotherGroup.add(new Circle("Inner Circle"));group.add(anotherGroup);// 绘制图形group.draw("");}
}

输出将类似于:

-Drawing ShapeGroup: MyShapes  -Drawing Circle: Circle  -Drawing Rectangle: Rectangle  -Drawing ShapeGroup: AnotherGroup  -Drawing Circle: Inner Circle

在这个Demo中,我们创建了一个简单的图形层次结构,其中ShapeGroup是一个复合图形,可以包含其他Shape对象,无论是基本的图形(如Circle和Rectangle)还是其他复合图形。通过这种方式,我们可以构建复杂的图形结构,并使用统一的接口来操作它们。

5. 外观模式(Facade Pattern)

在Java中,外观模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

以下是一个简单的Java外观模式示例,我们将模拟一个系统,该系统包括几个不同的组件(如日志记录器、数据库访问器等),我们将通过一个外观类来简化对这些组件的访问。

首先,我们定义几个简单的组件接口及其实现:

// 日志记录器接口
interface Logger {void log(String message);
}// 日志记录器实现
class SimpleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Logging: " + message);}
}// 数据库接口
interface Database {void connect();void disconnect();
}	// 数据库实现
class SimpleDatabase implements Database {@Overridepublic void connect() {System.out.println("Connecting to database...");}@Overridepublic void disconnect() {System.out.println("Disconnecting from database...");}
}

接下来,我们定义一个外观类,该类将上述组件的接口组合成一个更简单的接口:

// 外观类
class SystemFacade {private Logger logger;private Database database;public SystemFacade() {this.logger = new SimpleLogger();this.database = new SimpleDatabase();}// 执行一个业务操作,使用到了日志和数据库public void performTask() {// 使用日志logger.log("Starting task...);// 使用数据库database.connect();// 假设这里执行了一些数据库操作System.out.println("Performing database operations...");database.disconnect();// 结束日志logger.log("Task completed...");}
}

最后,我们通过一个简单的测试类来演示如何使用这个外观类:

public class FacadeDemo {public static void main(String[] args) {// 创建一个外观类的实例SystemFacade facade = new SystemFacade();// 调用外观类的方法执行业务操作facade.performTask();}
}

在这个示例中,SysytemFacade 类提供了一个简单的方法来执行一个涉及日志和数据库操作的业务任务。客户端代码(在这个例子中是 FacadeDemo 类)只需要与 SystemFacade 类交互,而不需要直接与 Logger 和
Database 接口的实现类交互,从而简化了客户端代码并降低了其与子系统内部细节的耦合度。

6. 享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。它通过共享已有的相似对象来避免创建大量重复的实例。在Java中实现享元模式通常涉及以下三个主要角色:

1.Flyweight(抽象享元类):定义了一个接口,通过这个接口可以访问并可能修改一个内部状态(不共享的部分),但通常由Context来维护。

2.ConcreteFlyweight(具体享元类):实现了Flyweight接口,并为内部状态(不共享)和可能有的共享状态提供存储。

3.FlyweightFactory(享元工厂类):负责创建和管理享元对象。它确保合理地共享享元,当用户请求一个享元时,Factory会检查是否已经存在一个符合要求的享元对象,如果存在,就返回这个已有的享元对象,如果不存在,就创建一个新的享元对象。

下面是一个简单的Java实现示例,其中享元对象代表不同的字符(字符的绘制可以通过简单的字符串表示,为了简化,我们这里直接用字符作为享元对象):

import java.util.HashMap;
import java.util.Map;// 抽象享元类
interface CharacterFlyweight {void display(String context);
}// 具体享元类
class UnicodeCharacter implements CharacterFlyweight {private char character;public UnicodeCharacter(char character) {this.character = character;}@Overridepublic void display(String context) {System.out.println("Displaying '" + character + "' + context);}
}// 共享工厂类
class CharaterFactory {private Map<Character, CharacterFlyweight> flyweights = new HashMap();public CharacterFlyweight getFlyweight(char key) {CharacterFlyweight fw = flyweight.get(key);if(fw == null) {fw = new UnicodeCharater(key);flyweights.put(key, fw);}return fw;}
}// 享元模式的使用
public class FlyweightPatternDemo {private static final String CONTEXT_A = "Font context A";private static final String CONTEXT_B = "Font context B";public static void main(String[] args) {CharacterFactory factory = new CharacterFactory();CharacterFlyweight fw1 = factory.getFlyweight('A');fw1.display(CONTEXT_A);CharacterFlyweight fw2 = factory.getFlyweight('B');fw2.display(CONTEXT_B);// 尝试获取相同的字符对象CharacterFlyweight fw3 = factory.getFlyweight('A');fw3.dispaly(CONTEXT_A + "again");// 检查是否指向同一个对象System.out.println(fw1 == fw3);// 应该输出 true}
}

在这个例子中,CharacterFlyweight 是一个接口,定义了所有享元对象共有的方法。UnicodeCharacter 是实现了 CharacterFlyweight 接口的具体享元类,代表了一个具体的字符对象。CharacterFactory 是享元工厂类,它负责创建和管理享元对象。通过检查已存在的对象来避免重复创建相同的字符对象。最后,在 FlyweightPatternDemo 类中展示了如何使用享元模式。

7.代理模式(Proxy Pattern)

在Java中实现代理模式(Proxy Pattern)主要有两种形式:静态代理和动态代理。静态代理是通过创建一个代理类来显式地指定要代理的方法,而动态代理则是在运行时动态地创建代理类。这里,我们将展示如何使用Java的动态代理来实现一个见得EDMO(这里假设EDMO是一个需要被代理的类,用于演示目的)的代理模式。

首先,我们需要一个接口(Interface)来定义EDMO的行为,然后实现这个接口的类就是EDMO的真实对象。接着我们使用Java的 Proxy 类和 InvocationHandler 接口来创建EDMO的动态代理。

步骤 1:定义EDMO接口

public interface EDMO {void performAction();
}

步骤 2:实现EDMO接口

public class RealEDMO implements EDMO {@Overridepublic void perormAction() {System.out.println("EDMO is performing an action...");}
}

步骤 3:创建动态代理的InvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class EDMOInvocationHandler implements InvocationHandler {private Object target;public EDMOInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在调用目标方法之前可以添加一些逻辑System.out.println("Before method: " method.getName());// 调用目标方法Object result = method.invoke(target, args);// 在调用目标方法之后可以添加一些逻辑System.out.println("After method: " + method.getName());return result;}
}

步骤 4:创建EDMO的代理对象并调用

import java.lang.reflect.Proxypublic class ProxyDemo {public static void main(String[] args) {// 创建EDMO的真实对象EDMO realEDMO = new RealEDMO();// 使用Proxy类和InvocationHandler来创建代理对象EDMO proxyEDMO = (EDMO) Proxy.newProxyInstance(realEDMO.getClass().getClassLoader(),new Class[]{EDMO.class},new EDMOInvocationHandler(realEDMO))// 通过代理对象调用方法proxyEDMO.performAction();};}

在这个例子中,EDMOInvocationHandler 是代理逻辑的处理器,它实现了 InvocationHandler 接口。当通过代理对象调用 performAction 方法时,实际上是通过 invoke 方法调用真是对象的 performAction 方法,同时在调用前后添加了额外的逻辑。

这就是使用Java动态代理模式来实现EDMO代理的一个基本示例。动态代理在需要为多个类创建代理时特别有用,因为它避免了为每个类手动编写静态代理的重复工作。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Elasticsearch 里的父子文档插入和查询
  • upload-labs通关攻略
  • jetson orin nx安装todesk
  • Matlab三维图的坐标轴标签 自动平行坐标/自动旋转
  • Android耗电优化,如何定位问题,如何修改
  • Unity学习路线
  • vscode上传自己开发的npm包
  • 哈希表(模拟实现)
  • Linux基础指令(2)
  • Mysql 巧秒避开 varchar 类型的 max()、min() 函数的坑
  • CSS中响应式设计
  • 利用衍射进行材料分析--Muad
  • 【Java】—— Java面向对象进阶:Java中的多态、继承与类型判断- instanceof 操作符与方法重载的模拟
  • MySQL基础学习:如何排查慢SQL
  • 什么是CAP理论和BASE思想?
  • [iOS]Core Data浅析一 -- 启用Core Data
  • 4个实用的微服务测试策略
  • canvas 五子棋游戏
  • Hibernate最全面试题
  • js ES6 求数组的交集,并集,还有差集
  • k8s如何管理Pod
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • PaddlePaddle-GitHub的正确打开姿势
  • Puppeteer:浏览器控制器
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • vue自定义指令实现v-tap插件
  • 安卓应用性能调试和优化经验分享
  • 初识 webpack
  • 从伪并行的 Python 多线程说起
  • 马上搞懂 GeoJSON
  • 前端代码风格自动化系列(二)之Commitlint
  • 如何使用 JavaScript 解析 URL
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 手机端车牌号码键盘的vue组件
  • 异步
  • 怎么把视频里的音乐提取出来
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​插件化DPI在商用WIFI中的价值
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • #14vue3生成表单并跳转到外部地址的方式
  • #Java第九次作业--输入输出流和文件操作
  • #数学建模# 线性规划问题的Matlab求解
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (C语言)fread与fwrite详解
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (附源码)springboot电竞专题网站 毕业设计 641314
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (六)vue-router+UI组件库
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (十三)Maven插件解析运行机制
  • (转)c++ std::pair 与 std::make
  • .NET CLR Hosting 简介