在软件设计中,结构型设计模式用于解决系统中对象和类之间的结构问题。这些模式帮助我们更好地组织代码,使其更具灵活性、可维护性和可扩展性。以下是几种常见的结构型设计模式的详解。
1. 适配器模式(Adapter)
定义:适配器模式的作用是将一个类的接口转换成用户希望得到的另一种接口,使得原本不兼容的接口能够协同工作。
解析:想象一下,你有一个新的笔记本电脑,它只有USB-C接口,但你有一些老式的USB-A设备。如果你想继续使用这些设备,你需要一个USB-C转USB-A的适配器。适配器模式在软件设计中扮演的就是这样的角色。它允许两个本来不兼容的类通过一个“适配器”类进行合作。
速记句:适配器让不兼容的接口能够一起工作。
// 代码示例:
public interface Target {
void request();
}
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specific request.");
}
}
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
2. 桥接模式(Bridge)
定义:桥接模式将抽象部分与它的实现部分分离,使它们可以独立变化,从而减少它们之间的耦合。
解析:桥接模式可以理解为将“桥”架在抽象和具体实现之间,使得它们可以独立变化。例如,假设你有一个遥控器类和多个不同的设备类(如电视、音响),桥接模式可以让你在无需更改遥控器类的情况下,轻松扩展或更改设备类。
速记句:桥接让抽象和实现可以独立变化。
// 代码示例:
public abstract class RemoteControl {
protected Device device;
public RemoteControl(Device device) {
this.device = device;
}
public abstract void pressButton();
}
public class TVRemoteControl extends RemoteControl {
public TVRemoteControl(Device device) {
super(device);
}
@Override
public void pressButton() {
device.operate();
}
}
public interface Device {
void operate();
}
public class TV implements Device {
@Override
public void operate() {
System.out.println("TV is operating.");
}
}
3. 组合模式(Composite)
定义:组合模式将对象组合成树形结构来表示整体与部分的关系,这样用户可以统一地处理单个对象和组合对象。
解析:想象你有一个文件系统,里面有文件和文件夹。文件夹里面可以包含文件或其他文件夹。这种结构的特点是你可以对文件和文件夹进行统一的操作,比如计算它们的大小。组合模式就是用来处理这种整体与部分关系的。
速记句:组合让你以统一的方式处理整体与部分。
// 代码示例:
public interface Component {
void operation();
}
public class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf operation.");
}
}
public class Composite implements Component {
private List<Component> children = new ArrayList<>();
public void add(Component component) {
children.add(component);
}
public void remove(Component component) {
children.remove(component);
}
@Override
public void operation() {
for (Component component : children) {
component.operation();
}
}
}
4. 装饰模式(Decorator)
定义:装饰模式动态地给一个对象添加一些额外的职责,提供了一种比继承更灵活的扩展功能的方式。
解析:举个例子,你有一杯咖啡,想要加奶和糖。你可以通过装饰模式来实现这一点,而不必为每种组合创建新的类。装饰模式允许你在运行时动态地为对象添加新的功能。
速记句:装饰动态地为对象添加功能。
// 代码示例:
public interface Coffee {
String getDescription();
double cost();
}
public class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple Coffee";
}
@Override
public double cost() {
return 2.0;
}
}
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double cost() {
return decoratedCoffee.cost();
}
}
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", Milk";
}
@Override
public double cost() {
return super.cost() + 0.5;
}
}
5. 外观模式(Facade)
定义:外观模式定义一个高层接口,为子系统中的一组接口提供一个统一的界面,简化了客户端与子系统的交互。
解析:想象你去餐厅点餐,你只需要和服务员打交道,而不需要直接与厨师、采购员等人沟通。服务员就相当于外观模式中的“外观类”,它为你简化了与餐厅整个系统的交互。
速记句:外观简化了子系统与外界的交互。
// 代码示例:
public class Facade {
private SubsystemOne subsystemOne;
private SubsystemTwo subsystemTwo;
public Facade() {
subsystemOne = new SubsystemOne();
subsystemTwo = new SubsystemTwo();
}
public void operation() {
subsystemOne.operationOne();
subsystemTwo.operationTwo();
}
}
public class SubsystemOne {
public void operationOne() {
System.out.println("Subsystem One operation.");
}
}
public class SubsystemTwo {
public void operationTwo() {
System.out.println("Subsystem Two operation.");
}
}
6. 享元模式(Flyweight)
定义:享元模式通过共享技术来支持大量细粒度对象的复用,以减少创建对象的数量。
解析:假设你有一个绘图应用,里面需要大量绘制不同颜色的小圆点。如果每个圆点都创建一个新的对象,会非常消耗内存。享元模式允许你共享颜色相同的圆点对象,以减少内存占用。
速记句:享元通过共享减少对象创建。
// 代码示例:
public class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String key) {
if (!flyweights.containsKey(key)) {
flyweights.put(key, new ConcreteFlyweight(key));
}
return flyweights.get(key);
}
}
public interface Flyweight {
void operation();
}
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation() {
System.out.println("Flyweight with state: " + intrinsicState);
}
}
7. 代理模式(Proxy)
定义:代理模式为其他对象提供一种代理以控制对这个对象的访问,可以用来实现延迟加载、访问控制等。
解析:想象你有一个大型图片文件,你不想每次打开程序时都立即加载它。代理模式可以帮助你在需要时才加载图片,而不是一开始就加载。这种模式可以帮助你节省资源,并控制对象的访问。
速记句:代理控制对象的访问。
// 代码示例:
public interface Image {
void display();
}
public class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + filename);
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
public 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();
}
}
总结
结构型设计模式是组织类和对象的一种重要方式,旨在提高代码的可复用性、可维护性和灵活性。通过适配器模式,我们可以让不兼容的接口协同工作;桥接模式则让抽象和实现部分能够独立变化;组合模式帮助我们统一处理整体和部分的关系;装饰模式提供了动态扩展对象功能的方法;外观模式简化了子系统与外界的交互;享元模式通过共享减少了对象创建的开销;而代理模式则控制了对象的访问。这些模式各有其独特的应用场景和优势,掌握它们可以极大地提升系统设计的质量。
参考文献
- Gamma, Erich, et al. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994.
- Freeman, Eric, et al. Head First Design Patterns. O'Reilly Media, 2004.