前言
这是设计模式的第二期;继续根据实际开发应用场景解析这几种结构型设计模式.
1.适配器模式(Adapter)
概念:
它允许两个不兼容的接口通过适配器类工作在一起。这种模式通常用于将已存在的类(被称为适配者)的接口转换成客户端期望的另一个接口。它可以让原本由于接口不匹配而不能一起工作的类能够协同工作。
实际使用场景:
假设我们正在开发一个在线音乐播放器应用,该应用需要兼容多种音频文件的播放。应用内部有一个标准的MediaPlayer接口,但现有的一些音频解码库(如一个只支持MP3格式的解码器MP3Decoder)并没有实现这个接口,而是有自己的播放方法。为了使这些库能够与我们的播放器框架集成,我们可以使用适配器模式来创建适配器类。
直接上代码:
a.原有接口及实现
java
// 原有的音频解码器接口,只支持播放MP3文件
public interface MP3Decoder {
void playMP3(String fileName);
}
java
public class ExistingMP3Decoder implements MP3Decoder {
@Override
public void playMP3(String fileName) {
System.out.println("Playing MP3 file. Filename: " + fileName);
}
}
b.目标接口
java
// 应用中标准的媒体播放器接口
public interface MediaPlayer {
void play(String audioType, String fileName);
}
c.适配器类
java
public class MediaAdapter implements MediaPlayer {
private MP3Decoder mp3Decoder;
public MediaAdapter() {
this.mp3Decoder = new ExistingMP3Decoder();
}
@Override
public void play(String audioType, String fileName) {
if ("mp3".equalsIgnoreCase(audioType)) {
mp3Decoder.playMP3(fileName);
} else {
System.out.println("Unsupported audio type.");
}
}
}
d.客户端代码
java
public class MusicPlayer {
public static void main(String[] args) {
MediaPlayer mediaPlayer = new MediaAdapter();
mediaPlayer.play("mp3", "favorite_song.mp3");
}
}
2.桥接模式(Bridge)
概念:
它将抽象部分与其实现部分分离开来,使它们可以独立变化。这种模式通过创建一个抽象类和一个实现类的层次结构,以及一个抽象类引用实现类的对象,来实现二者的解耦。桥接模式适用于希望在不修改抽象和实现的前提下,让两者都能独立变化的场景。
实际使用场景:
考虑一个图形编辑软件,需要支持多种形状(如圆形、矩形)的绘制,并且这些形状需要能够在多种设备上显示(如普通屏幕、高分辨率屏幕)。为了实现这样的需求,我们可以使用桥接模式来设计系统,让形状的定义和显示逻辑分开,从而使得形状的种类和显示设备可以独立扩展。
直接上代码:
a.抽象形状与具体形状
java
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();
}
java
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;
}
@Override
public void draw() {
drawingAPI.drawCircle(x, y, radius);
}
}
java
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;
}
@Override
public void draw() {
drawingAPI.drawRect(x, y, width, height);
}
}
b.显示接口与具体显示实现
java
public interface DrawingAPI {
void drawCircle(int x, int y, int radius);
void drawRect(int x, int y, int width, int height);
}
java
public class DrawingAPI1 implements DrawingAPI {
@Override
public void drawCircle(int x, int y, int radius) {
System.out.println("API1: Drawing Circle at (" + x + "," + y + ") with radius " + radius);
}
@Override
public 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);
}
}
java
public class DrawingAPI2 implements DrawingAPI {
@Override
public void drawCircle(int x, int y, int radius) {
System.out.println("API2 (High-res): Drawing Circle at (" + x + "," + y + ") with radius " + radius);
}
@Override
public 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.客户端代码
java
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)
java
public interface FileSystemItem {
String getName();
void printStructure(int depth);
}
b.叶子组件(Leaf)
java
public class File implements FileSystemItem {
private String name;
public File(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void printStructure(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("\t");
}
System.out.println(name);
}
}
c.复合组件(Composite)
java
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);
}
@Override
public String getName() {
return name;
}
@Override
public 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.客户端代码
java
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)
java
public interface TextDisplay {
// 显示文本的基本方法
void display();
}
b.具体组件(Concrete Component)
java
public class SimpleTextDisplay implements TextDisplay {
private String text;
public SimpleTextDisplay(String text) {
this.text = text;
}
@Override
public void display() {
System.out.println(text);
}
}
c.装饰器接口(Decorator)
java
public interface TextDisplayDecorator extends TextDisplay {
// 引入基础组件的引用,以便于装饰器可以调用被装饰对象的方法
TextDisplay getWrappedDisplay();
void setWrappedDisplay(TextDisplay wrappedDisplay);
}
d.具体装饰器
java
public class BoldDecorator implements TextDisplayDecorator {
private TextDisplay wrappedDisplay;
public BoldDecorator(TextDisplay wrappedDisplay) {
this.wrappedDisplay = wrappedDisplay;
}
@Override
public void display() {
System.out.print("[Bold] ");
wrappedDisplay.display();
}
@Override
public TextDisplay getWrappedDisplay() {
return wrappedDisplay;
}
@Override
public void setWrappedDisplay(TextDisplay wrappedDisplay) {
this.wrappedDisplay = wrappedDisplay;
}
}
java
public class ItalicDecorator implements TextDisplayDecorator {
private TextDisplay wrappedDisplay;
public ItalicDecorator(TextDisplay wrappedDisplay) {
this.wrappedDisplay = wrappedDisplay;
}
@Override
public void display() {
System.out.print("[Italic] ");
wrappedDisplay.display();
}
@Override
public TextDisplay getWrappedDisplay() {
return wrappedDisplay;
}
@Override
public void setWrappedDisplay(TextDisplay wrappedDisplay) {
this.wrappedDisplay = wrappedDisplay;
}
}
e.客户端代码
java
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.子系统类
java
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.");
}
}
java
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)
java
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.客户端代码
java
public class FacadePatternDemo {
public static void main(String[] args) {
MultimediaFacade facade = new MultimediaFacade();
facade.startMovie(); // 启动电影播放
// ...
facade.endMovie(); // 结束电影播放
}
}
6.享元模式(Flyweight)
概念:
用于通过共享技术有效支持大量细粒度对象的复用。它通过存储对象的外部状态与内部状态分离,使得多个对象可以共享同一个内部状态,从而减少内存占用,提高效率。享元模式常用于处理大量相似对象的场景,如文档编辑器中的字符、游戏中的子弹等。
实际使用场景:
想象一下开发一个文字处理软件,其中包含大量的字符对象,每个字符可能有多种字体、字号和颜色。如果不使用享元模式,为每个字符创建独立的对象将会非常消耗内存。通过享元模式,我们可以共享字符的内部状态(如字符的形状信息),而外部状态(如位置、颜色)则在使用时动态指定。
直接上代码:
a.抽象享元类
java
public interface Character {
void display(int pointSize, String color); // 显示字符,pointSize和color是外部状态
}
b.具体享元类
java
public class CharacterA implements Character {
@Override
public void display(int pointSize, String color) {
System.out.println("Displaying 'A' with point size " + pointSize + " and color " + color);
}
}
c.享元工厂
java
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.客户端代码
java
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)
java
public interface VideoPlayer {
void playVideo(String videoName);
}
b.真实主题(RealSubject)
java
public class RealVideoPlayer implements VideoPlayer {
@Override
public void playVideo(String videoName) {
System.out.println("Playing video: " + videoName);
}
}
c.代理(Proxy)
java
public class VideoPlayerProxy implements VideoPlayer {
private RealVideoPlayer realVideoPlayer;
private User user;
public VideoPlayerProxy(User user) {
this.user = user;
}
@Override
public 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.用户类(示例)
java
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.客户端代码
java
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"); // 应输出播放免费视频
}
}
好了以上就是结构型设计模式的几种具体设计模式的使用场景;下期再见.