享元模式 Flyweight
1、什么是享元模式
享元模式的核心思想是共享对象,即通过尽可能多地共享相似对象来减少内存占用或计算开销。这意味着相同或相似的对象在内存中只存在一个共享实例。
2、为什么使用享元模式
- 减少内存使用:通过共享相似对象,减少了系统中对象的数量,从而减少了内存的使用。
- 提高性能:由于共享对象减少了创建和销毁的开销,提高了系统的性能。
- 简化代码:享元模式使得系统中的对象更加简单,因为需要相似的对象可以共享相同的状态。
3、如何使用享元模式
设计和实现一个文本编辑器,针对大量相似的字符对象,通过享元模式来共享相同字符对象
import java.util.HashMap;
import java.util.Map;
// 抽象享元角色
interface CharFlyweight {
void display();
}
// 具体享元角色
class ConcreteCharFlyweight implements CharFlyweight {
private char character;
public ConcreteCharFlyweight(char character) {
this.character = character;
}
@Override
public void display() {
System.out.print(character);
}
}
// 享元工厂
class CharFlyweightFactory {
private Map<Character, CharFlyweight> flyweights = new HashMap<>();
public CharFlyweight getCharFlyweight(char character) {
if (!flyweights.containsKey(character)) {
flyweights.put(character, new ConcreteCharFlyweight(character));
}
return flyweights.get(character);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
CharFlyweightFactory factory = new CharFlyweightFactory();
CharFlyweight a = factory.getCharFlyweight('a');
CharFlyweight b = factory.getCharFlyweight('b');
CharFlyweight aAgain = factory.getCharFlyweight('a');
a.display(); // 输出:a
b.display(); // 输出:b
aAgain.display(); // 输出:a
// 对比对象是否相同
System.out.println("\nIs a the same object as aAgain? " + (a == aAgain));
}
}
4、是否存在缺陷和不足
- 共享状态限制:享元模式主要适用于具有大量相似对象的情况,且这些对象可以共享状态。如果对象的状态不可共享,则不适合使用享元模式。
- 复杂性增加:在一些场景下,为了实现共享,需要将对象的一部分状态外部话,导致系统的复杂性增加。
5、如何缓解缺陷和不足
- 外部状态和内部状态分离:尽量将对象的外部状态(不可共享的状态)和内部状态(可共享的状态)清晰地分离,以便在不同的环境中使用。
- 合理使用享元模式:只有在系统中存在大量相似对象且能够共享状态时,才考虑使用享元模式,在其他情况下,可能并不切实际。
代理模式 Proxy
1、什么是代理模式
代理模式通过引入代理对象来控制对其他对象的访问。代理对象充当被代理对象的接口,可以在访问的时候添加额外的功能,比如安全性检查、缓存引入等操作功能。
2、为什么使用代理模式
- 控制访问:代理模式允许在访问对象时进行控制,可以添加额外的逻辑,比如权限检查、缓存引入等。
- 解耦:代理模式将客户端与目标对象解耦,客户端无需直接访问目标对象,降低了系统的耦合度。
- 延迟加载:代理模式可以实现延迟加载,在需要的时候再创建目标对象,提高系统的性能。
3、如何使用代理模式
设计实现一个 Image 接口,具体的实现类是 RealImage。通过代理模式,因为 ProxyImage 来控制对 RealImage 的访问
// 目标接口
interface Image {
void display();
}
// 具体目标类
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// 代理类
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Image image = new ProxyImage("example.jpg");
// 图像第一次加载
image.display();
// 图像第二次加载,使用缓存
image.display();
}
}
4、是否存在缺陷和不足
- 复杂性增加:引入代理对象可能会增加系统的复杂性,因为需要额外的类来管理代理和目标对象。
- 性能开销:在某些情况下,代理模式可能引入一定的性能开销,尤其是在代理对象的创建和销毁方面。
5、如何缓解缺陷和不足
- 智能代理:使用智能代理,根据具体情况选择是否创建目标对象,什么时候需要什么时候创建。
- 动态代理:使用动态代理,通过反射机制动态创建代理对象,减少手动创建代理对象的复杂性。
桥接模式 Bridge
1、什么是桥接模式
桥接模式的核心思想是将抽象部分和实现部分分离,使它们可以独立地发生变化。通过抽象类和实现类设计为两个独立的层次方案,并使用组合组合关系将他们连接起来。
2、为什么使用桥接模式
- 解耦抽象和实现:桥接模式将抽象和实现分离,使得他们可以独立地发生变化,降低系统的耦合度。
- 提高灵活性:桥接模式提高了系统的灵活性,可以更容易地添加新的抽象类和实现类。
- 复用性:可以在不修改已有代码的情况下引入新的抽象和实现。
3、如何使用桥接模式
设计实现一个图形绘制的场景,有不同类型的图形和不同的绘制方式,要求通过桥接模式将图形和绘制方式解耦。
// 实现部分 - 绘制方式
interface DrawingAPI {
void drawCircle(int x, int y, int radius);
}
// 具体实现部分 - 红色绘制方式
class RedDrawingAPI implements DrawingAPI {
@Override
public void drawCircle(int x, int y, int radius) {
System.out.println("Drawing Red Circle at (" + x + "," + y + ") with radius " + radius);
}
}
// 具体实现部分 - 绿色绘制方式
class GreenDrawingAPI implements DrawingAPI {
@Override
public void drawCircle(int x, int y, int radius) {
System.out.println("Drawing Green Circle at (" + x + "," + y + ") with radius " + radius);
}
}
// 抽象部分 - 图形
abstract class Shape {
protected DrawingAPI drawingAPI;
protected Shape(DrawingAPI drawingAPI) {
this.drawingAPI = drawingAPI;
}
abstract void draw();
}
// 具体抽象部分 - 圆形
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
void draw() {
drawingAPI.drawCircle(x, y, radius);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
DrawingAPI redAPI = new RedDrawingAPI();
DrawingAPI greenAPI = new GreenDrawingAPI();
Shape redCircle = new Circle(100, 100, 10, redAPI);
Shape greenCircle = new Circle(200, 200, 20, greenAPI);
redCircle.draw();
greenCircle.draw();
}
}
4、是否存在缺陷和不足
- 增加复杂性:桥接模式可能会增加系统的复杂性,因为需要设计多个抽象类和实现的类层次结构。
5、如何缓解缺陷和不足
- 谨慎设计类的层次结构:设计抽象和实现的类层次结构时,需要谨慎考虑,避免过度设计。
- 使用合适的模式:根据系统的需求,考虑其他结构型模式,选择最适合的模式。