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

浅谈Java23种设计模式之结构型模式的几种使用场景

前言
这是设计模式的第二期;继续根据实际开发应用场景解析这几种结构型设计模式.

1.适配器模式(Adapter)

概念:

它允许两个不兼容的接口通过适配器类工作在一起。这种模式通常用于将已存在的类(被称为适配者)的接口转换成客户端期望的另一个接口。它可以让原本由于接口不匹配而不能一起工作的类能够协同工作。

实际使用场景:

假设我们正在开发一个在线音乐播放器应用,该应用需要兼容多种音频文件的播放。应用内部有一个标准的MediaPlayer接口,但现有的一些音频解码库(如一个只支持MP3格式的解码器MP3Decoder)并没有实现这个接口,而是有自己的播放方法。为了使这些库能够与我们的播放器框架集成,我们可以使用适配器模式来创建适配器类。

直接上代码:

a.原有接口及实现

// 原有的音频解码器接口,只支持播放MP3文件
public interface MP3Decoder {void playMP3(String fileName);
}
public class ExistingMP3Decoder implements MP3Decoder {@Overridepublic void playMP3(String fileName) {System.out.println("Playing MP3 file. Filename: " + fileName);}
}

b.目标接口

// 应用中标准的媒体播放器接口
public interface MediaPlayer {void play(String audioType, String fileName);
}

c.适配器类

public class MediaAdapter implements MediaPlayer {private MP3Decoder mp3Decoder;public MediaAdapter() {this.mp3Decoder = new ExistingMP3Decoder();}@Overridepublic void play(String audioType, String fileName) {if ("mp3".equalsIgnoreCase(audioType)) {mp3Decoder.playMP3(fileName);} else {System.out.println("Unsupported audio type.");}}
}

d.客户端代码

public class MusicPlayer {public static void main(String[] args) {MediaPlayer mediaPlayer = new MediaAdapter();mediaPlayer.play("mp3", "favorite_song.mp3");}
}

2.桥接模式(Bridge)

概念:

它将抽象部分与其实现部分分离开来,使它们可以独立变化。这种模式通过创建一个抽象类和一个实现类的层次结构,以及一个抽象类引用实现类的对象,来实现二者的解耦。桥接模式适用于希望在不修改抽象和实现的前提下,让两者都能独立变化的场景。

实际使用场景:

考虑一个图形编辑软件,需要支持多种形状(如圆形、矩形)的绘制,并且这些形状需要能够在多种设备上显示(如普通屏幕、高分辨率屏幕)。为了实现这样的需求,我们可以使用桥接模式来设计系统,让形状的定义和显示逻辑分开,从而使得形状的种类和显示设备可以独立扩展。

直接上代码:

a.抽象形状与具体形状

public abstract class Shape {protected DrawingAPI drawingAPI;protected Shape(DrawingAPI drawingAPI) {this.drawingAPI = drawingAPI;}public abstract void draw();public abstract void resizeByPercentage(double pct);public abstract String getDescription();
}
public class Circle extends Shape {private int x, y, radius;public Circle(int x, int y, int radius, DrawingAPI drawingAPI) {super(drawingAPI);this.x = x;this.y = y;this.radius = radius;}@Overridepublic void draw() {drawingAPI.drawCircle(x, y, radius);}
}
public class Rectangle extends Shape {private int x, y, width, height;public Rectangle(int x, int y, int width, int height, DrawingAPI drawingAPI) {super(drawingAPI);this.x = x;this.y = y;this.width = width;this.height = height;}@Overridepublic void draw() {drawingAPI.drawRect(x, y, width, height);}
}

b.显示接口与具体显示实现

public interface DrawingAPI {void drawCircle(int x, int y, int radius);void drawRect(int x, int y, int width, int height);
}
public class DrawingAPI1 implements DrawingAPI {@Overridepublic void drawCircle(int x, int y, int radius) {System.out.println("API1: Drawing Circle at (" + x + "," + y + ") with radius " + radius);}@Overridepublic void drawRect(int x, int y, int width, int height) {System.out.println("API1: Drawing Rectangle at (" + x + "," + y + ") with width " + width + " and height " + height);}
}
public class DrawingAPI2 implements DrawingAPI {@Overridepublic void drawCircle(int x, int y, int radius) {System.out.println("API2 (High-res): Drawing Circle at (" + x + "," + y + ") with radius " + radius);}@Overridepublic void drawRect(int x, int y, int width, int height) {System.out.println("API2 (High-res): Drawing Rectangle at (" + x + "," + y + ") with width " + width + " and height " + height);}
}

c.客户端代码

public class BridgePatternDemo {public static void main(String[] args) {Shape circle = new Circle(100, 100, 20, new DrawingAPI1());Shape rectangle = new Rectangle(150, 150, 100, 50, new DrawingAPI2());System.out.println("Drawing shapes:");circle.draw();rectangle.draw();}
}

3.组合模式(Composite)

概念:

它允许你将对象组合成树状结构来表示整体与部分的层次关系。它可以使客户端以一致的方式处理单个对象和组合对象,而无需关心处理的是单个对象还是组合对象。这个模式非常适合用于表示具有层次结构的数据,如文件系统、组织结构等。

实际使用场景:

假设我们正在开发一个文件管理器应用,需要展示文件和文件夹的层级结构,并能够对它们执行一些基本操作,如打印名称。这里,文件夹可以看作是包含多个文件和子文件夹的组合对象,而文件则是叶子对象。

直接上代码:

a.抽象组件(Component)

public interface FileSystemItem {String getName();void printStructure(int depth);
}

b.叶子组件(Leaf)

public class File implements FileSystemItem {private String name;public File(String name) {this.name = name;}@Overridepublic String getName() {return name;}@Overridepublic void printStructure(int depth) {for (int i = 0; i < depth; i++) {System.out.print("\t");}System.out.println(name);}
}

c.复合组件(Composite)

import java.util.ArrayList;
import java.util.List;public class Folder implements FileSystemItem {private String name;private List<FileSystemItem> children = new ArrayList<>();public Folder(String name) {this.name = name;}public void addItem(FileSystemItem item) {children.add(item);}@Overridepublic String getName() {return name;}@Overridepublic void printStructure(int depth) {for (int i = 0; i < depth; i++) {System.out.print("\t");}System.out.println(name);for (FileSystemItem child : children) {child.printStructure(depth + 1);}}
}

d.客户端代码

public class CompositePatternDemo {public static void main(String[] args) {Folder rootFolder = new Folder("root");Folder documentsFolder = new Folder("Documents");Folder picturesFolder = new Folder("Pictures");File file1 = new File("report.txt");File file2 = new File("image1.jpg");File file3 = new File("image2.jpg");documentsFolder.addItem(file1);picturesFolder.addItem(file2);picturesFolder.addItem(file3);rootFolder.addItem(documentsFolder);rootFolder.addItem(picturesFolder);rootFolder.printStructure(0);}
}

4.装饰器模式(Decorator)

概念:

它动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更为灵活。这种模式创建了一个装饰类,用来包装原有的类,并在保持原有类方法签名完整性的前提下,提供了额外的功能。

实际使用场景:

假设我们正在开发一个简单的文本编辑器,其中最基本的功能是显示文本。现在,我们想为文本增加一些格式化功能,比如加粗、斜体、颜色等,而不必为每种可能的文本格式创建一个新的类。这时,装饰器模式是一个很好的解决方案。

直接上代码:

a.基础组件(Component)

public interface TextDisplay {// 显示文本的基本方法void display();
}

b.具体组件(Concrete Component)

public class SimpleTextDisplay implements TextDisplay {private String text;public SimpleTextDisplay(String text) {this.text = text;}@Overridepublic void display() {System.out.println(text);}
}

c.装饰器接口(Decorator)

public interface TextDisplayDecorator extends TextDisplay {// 引入基础组件的引用,以便于装饰器可以调用被装饰对象的方法TextDisplay getWrappedDisplay();void setWrappedDisplay(TextDisplay wrappedDisplay);
}

d.具体装饰器

public class BoldDecorator implements TextDisplayDecorator {private TextDisplay wrappedDisplay;public BoldDecorator(TextDisplay wrappedDisplay) {this.wrappedDisplay = wrappedDisplay;}@Overridepublic void display() {System.out.print("[Bold] ");wrappedDisplay.display();}@Overridepublic TextDisplay getWrappedDisplay() {return wrappedDisplay;}@Overridepublic void setWrappedDisplay(TextDisplay wrappedDisplay) {this.wrappedDisplay = wrappedDisplay;}
}
public class ItalicDecorator implements TextDisplayDecorator {private TextDisplay wrappedDisplay;public ItalicDecorator(TextDisplay wrappedDisplay) {this.wrappedDisplay = wrappedDisplay;}@Overridepublic void display() {System.out.print("[Italic] ");wrappedDisplay.display();}@Overridepublic TextDisplay getWrappedDisplay() {return wrappedDisplay;}@Overridepublic void setWrappedDisplay(TextDisplay wrappedDisplay) {this.wrappedDisplay = wrappedDisplay;}
}

e.客户端代码

public class DecoratorPatternDemo {public static void main(String[] args) {// 创建一个原始的文本显示对象TextDisplay originalText = new SimpleTextDisplay("Hello, World!");// 使用装饰器模式为文本添加加粗和斜体效果TextDisplay decoratedText = new BoldDecorator(new ItalicDecorator(originalText));// 显示格式化后的文本decoratedText.display();}
}

5.外观模式(Facade)

概念:

它为子系统中的一组接口提供了一个一致的高层接口,简化了子系统的使用。这种模式通过一个单一的类(即外观类)封装了子系统的复杂性,使得客户端与子系统的交互变得简单。外观模式并不改变原有子系统的功能和结构,只是提供了一个更加友好的访问接口。

实际使用场景:

假设我们正在开发一个多媒体播放器应用,这个应用需要控制多种设备,比如音频播放器、视频播放器、投影仪、屏幕等。每种设备都有自己复杂的操作接口。为了简化客户端代码,减少与多个子系统的直接耦合,我们可以使用外观模式来设计一个多媒体控制器,它提供一个简单的接口来控制整个多媒体播放流程。

直接上代码:

a.子系统类

public class AudioPlayer {public void on() {System.out.println("Audio player is on.");}public void off() {System.out.println("Audio player is off.");}public void play() {System.out.println("Audio is playing.");}
}
public class VideoPlayer {public void on() {System.out.println("Video player is on.");}public void off() {System.out.println("Video player is off.");}public void play() {System.out.println("Video is playing.");}
}

b.外观类(Facade)

public class MultimediaFacade {private AudioPlayer audioPlayer;private VideoPlayer videoPlayer;// 可以添加更多子系统对象public MultimediaFacade() {this.audioPlayer = new AudioPlayer();this.videoPlayer = new VideoPlayer();}public void startMovie() {audioPlayer.on();videoPlayer.on();videoPlayer.play();// 可以添加更多协同操作System.out.println("Movie started.");}public void endMovie() {audioPlayer.off();videoPlayer.off();// 可以添加更多结束时的操作System.out.println("Movie ended.");}
}

c.客户端代码

public class FacadePatternDemo {public static void main(String[] args) {MultimediaFacade facade = new MultimediaFacade();facade.startMovie(); // 启动电影播放// ...facade.endMovie(); // 结束电影播放}
}

6.享元模式(Flyweight)

概念:

用于通过共享技术有效支持大量细粒度对象的复用。它通过存储对象的外部状态与内部状态分离,使得多个对象可以共享同一个内部状态,从而减少内存占用,提高效率。享元模式常用于处理大量相似对象的场景,如文档编辑器中的字符、游戏中的子弹等。

实际使用场景:

想象一下开发一个文字处理软件,其中包含大量的字符对象,每个字符可能有多种字体、字号和颜色。如果不使用享元模式,为每个字符创建独立的对象将会非常消耗内存。通过享元模式,我们可以共享字符的内部状态(如字符的形状信息),而外部状态(如位置、颜色)则在使用时动态指定。

直接上代码:

a.抽象享元类

public interface Character {void display(int pointSize, String color); // 显示字符,pointSize和color是外部状态
}

b.具体享元类

public class CharacterA implements Character {@Overridepublic void display(int pointSize, String color) {System.out.println("Displaying 'A' with point size " + pointSize + " and color " + color);}
}

c.享元工厂

import java.util.HashMap;
import java.util.Map;public class CharacterFactory {private Map<Character, Character> pool = new HashMap<>();public Character getCharacter(char character) {Character charInstance = pool.get(character);if (charInstance == null) {if (character == 'A') {charInstance = new CharacterA();}// 可以根据需要添加更多字符的创建逻辑pool.put(character, charInstance);}return charInstance;}
}

d.客户端代码

public class FlyweightPatternDemo {public static void main(String[] args) {CharacterFactory factory = new CharacterFactory();// 创建两个显示红色24号字体的"A"Character charA1 = factory.getCharacter('A');charA1.display(24, "Red");Character charA2 = factory.getCharacter('A');charA2.display(24, "Red");// 证明两个"A"共享同一内部状态System.out.println(charA1 == charA2); // 应输出true,表示它们是同一个对象}
}

7.代理模式(Proxy)

概念:

它为其他对象提供了一个替身或占位符,以便控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以用来延迟初始化、访问控制、增加额外功能或为远程服务提供本地代表等目的。

实际使用场景:

设想一个在线视频流媒体服务,用户可以观看不同类型的视频内容。为了提升用户体验和管理资源,服务端需要对视频的访问进行控制,例如,检查用户是否已经登录,是否具备观看特定视频的权限等。此时,可以使用代理模式设计一个代理类来控制对实际视频播放器对象的访问。

直接上代码:

a.抽象主题(Subject)

public interface VideoPlayer {void playVideo(String videoName);
}

b.真实主题(RealSubject)

public class RealVideoPlayer implements VideoPlayer {@Overridepublic void playVideo(String videoName) {System.out.println("Playing video: " + videoName);}
}

c.代理(Proxy)

public class VideoPlayerProxy implements VideoPlayer {private RealVideoPlayer realVideoPlayer;private User user;public VideoPlayerProxy(User user) {this.user = user;}@Overridepublic void playVideo(String videoName) {if (user.isLoggedIn() && user.hasPermission(videoName)) {if (realVideoPlayer == null) {realVideoPlayer = new RealVideoPlayer();}realVideoPlayer.playVideo(videoName);} else {System.out.println("Access denied. Please login or check your permissions.");}}
}

d.用户类(示例)

public class User {private boolean isLoggedIn;private boolean hasPremiumAccess;public User(boolean isLoggedIn, boolean hasPremiumAccess) {this.isLoggedIn = isLoggedIn;this.hasPremiumAccess = hasPremiumAccess;}public boolean isLoggedIn() {return isLoggedIn;}public boolean hasPermission(String videoName) {// 简化处理,假设高级用户有观看所有视频的权限return hasPremiumAccess || videoName.startsWith("Free_");}
}

e.客户端代码

public class ProxyPatternDemo {public static void main(String[] args) {User user = new User(true, false); // 已登录,但没有高级权限VideoPlayer proxy = new VideoPlayerProxy(user);proxy.playVideo("Paid_Content_01"); // 应输出访问被拒绝proxy.playVideo("Free_Content_01"); // 应输出播放免费视频}
}

好了以上就是结构型设计模式的几种具体设计模式的使用场景;下期再见.

相关文章:

  • 计算机专业毕设-springboot论坛系统
  • C语言实现五子棋教程
  • 内核学习——6、timer的学习和使用
  • 聊聊分布式集群的基本概念
  • AI工具对音乐的影响
  • 只有你相信,客户才会相信
  • Linux下调试代码——gdb的使用
  • 深入理解计算机系统 CSAPP 家庭作业6.34
  • 零基础入门学用Arduino 第四部分(三)
  • 【单片机毕业设计选题24003】-基于STM32和阿里云的家庭安全监测系统
  • ARM架构简明教程
  • 项目3:从0开始的RPC框架(扩展版)-3
  • STM32学习笔记(八)--DMA直接存储器存取详解
  • css display:grid布局,实现任意行、列合并后展示,自适应大小屏幕
  • VMR,支持30+种编程语言的SDK版本管理器,支持Windows/MacOS/Linux。
  • python3.6+scrapy+mysql 爬虫实战
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • Hibernate最全面试题
  • Invalidate和postInvalidate的区别
  • Java应用性能调优
  • Linux链接文件
  • Travix是如何部署应用程序到Kubernetes上的
  • vue 配置sass、scss全局变量
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 对JS继承的一点思考
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 聊聊directory traversal attack
  • 一个JAVA程序员成长之路分享
  • 《码出高效》学习笔记与书中错误记录
  • Java性能优化之JVM GC(垃圾回收机制)
  • k8s使用glusterfs实现动态持久化存储
  • Python 之网络式编程
  • ​数据链路层——流量控制可靠传输机制 ​
  • #宝哥教你#查看jquery绑定的事件函数
  • (2)空速传感器
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (python)数据结构---字典
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)ssm高校实验室 毕业设计 800008
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (一) 初入MySQL 【认识和部署】
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转)Linq学习笔记
  • (转载)PyTorch代码规范最佳实践和样式指南
  • ./configure,make,make install的作用
  • .equal()和==的区别 怎样判断字符串为空问题: Illegal invoke-super to void nio.file.AccessDeniedException
  • .Net 6.0--通用帮助类--FileHelper
  • .NET NPOI导出Excel详解
  • .Net 高效开发之不可错过的实用工具
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)