一、什么是设计模式
设计模式是在软件设计中反复出现的问题的通用解决方案。它们是经过多次验证和应用的指导原则,旨在帮助软件开发人员解决特定类型的问题,提高代码的可维护性、可扩展性和重用性。
设计模式是一种抽象化的思维方式,可以帮助开发人员更好地组织和设计他们的代码。它们提供了一种通用的框架,可以用于解决各种不同的软件设计问题。设计模式不是完整的代码,而是一种描述问题和解决方案之间关系的模板。
设计模式并不是一成不变的法则,而是根据不同的问题和情境来决定是否使用以及如何使用。了解和应用设计模式可以帮助开发人员更好地组织代码,提高代码的可读性和可维护性,同时也有助于促进团队之间的合作和沟通。
二、设计模式的分类
- 创建型模式(Creational):关注对象的实例化过程,包括了如何实例化对象、隐藏对象的创建细节等。常见的创建型模式有单例模式、工厂模式、抽象工厂模式等。
- 结构型模式(Structural):关注对象之间的组合方式,以达到构建更大结构的目标。这些模式帮助你定义对象之间的关系,从而实现更大的结构。常见的结构型模式有适配器模式、装饰器模式、代理模式等。
- 行为型模式(Behavioral):关注对象之间的通信方式,以及如何合作共同完成任务。这些模式涉及到对象之间的交互、责任分配等。常见的行为型模式有观察者模式、策略模式、命令模式等。
三、设计模式的基本要素
- 模式名称:每个设计模式都有一个简洁的名称,用于描述问题、解决方案和效果。这个名称有助于在交流中快速指代模式。
- 问题:描述了在什么情况下应该考虑使用特定的设计模式。问题部分阐述了该模式试图解决的具体设计难题。
- 解决方案:解决方案部分提供了一个详细的设计指南,描述了如何组织类、对象以及它们之间的关系,以解决特定问题。这包括了每个角色的职责、协作方式等。
- 效果:描述了模式应用的效果及使用模式应权衡的问题。
四、23 种设计模式概览
23种设计模式概览
五、设计模式间的关系
23种设计模式关系图
六、设计模式代码示例
以下包含Java版示例代码
七、设计模式详解
1. 工厂方法模式(Factory Method)
问题:
在软件设计中,我们经常遇到需要创建不同类型对象的情况。但是,如果直接在代码中实例化对象,会使代码紧密耦合在一起,难以维护和扩展。此外,如果对象的创建方式需要变化,那么就需要在整个代码中进行大量的修改。工厂方法模式旨在解决这个问题。
解决方案:
工厂方法模式提供了一个创建对象的接口,但是将具体的对象创建延迟到子类中。这样,客户端代码不需要知道要创建的具体对象的类,只需要通过工厂方法来创建对象。这使得客户端代码与具体对象的创建解耦,提高了代码的灵活性和可维护性。
在工厂方法模式中,通常会定义一个抽象工厂类,其中包含一个创建对象的抽象方法,而具体的对象创建则由具体的子类实现。这样,每个具体的子类都可以根据需要创建不同类型的对象,而客户端代码只需要通过抽象工厂类来调用工厂方法,而不需要关心具体的对象创建细节。
效果:
工厂方法模式的优点包括:
- 松耦合:客户端代码与具体对象的创建解耦,使得系统更具弹性和可维护性。
- 扩展性:通过添加新的具体工厂和产品子类,可以很容易地扩展系统以支持新的对象类型。
- 封装性:将对象的创建集中在工厂类中,封装了对象的创建细节,使得客户端代码更简洁。
然而,工厂方法模式也可能引入一些额外的复杂性,因为需要定义多个工厂类和产品类的层次结构。这可能会导致系统中类的数量增加。在选择使用工厂方法模式时,需要根据具体情况进行权衡。
工厂方法模式在实际应用中非常常见,例如,图形库可以使用工厂方法模式来创建不同类型的图形对象,数据库访问框架可以使用工厂方法模式来创建不同类型的数据库连接等。
代码示例:
// 首先,我们需要定义一个图形接口
interface Shape {
void draw();
}
// 然后,我们实现两个具体的图形类,分别是 Circle(圆形)和 Rectangle(矩形)
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
// 接下来,我们创建一个抽象工厂类 ShapeFactory
// 它定义了一个抽象的工厂方法 createShape,子类将实现这个方法来创建具体的图形对象
abstract class ShapeFactory {
abstract Shape createShape();
}
// 然后,我们创建两个具体的工厂类,分别是 CircleFactory 和 RectangleFactory
// 它们分别实现了 ShapeFactory 并重写了 createShape 方法来返回相应的图形对象
class CircleFactory extends ShapeFactory {
@Override
Shape createShape() {
return new Circle();
}
}
class RectangleFactory extends ShapeFactory {
@Override
Shape createShape() {
return new Rectangle();
}
}
// 我们可以使用这些工厂类来创建图形对象
public class FactoryMethodExample {
public static void main(String[] args) {
ShapeFactory circleFactory = new CircleFactory();
Shape circle = circleFactory.createShape();
circle.draw();
ShapeFactory rectangleFactory = new RectangleFactory();
Shape rectangle = rectangleFactory.createShape();
rectangle.draw();
}
}
2. 抽象工厂模式(Abstract Factory)
问题:
在某些情况下,需要创建一系列相关或相互依赖的对象,这些对象属于一组相关的产品族。同时,系统需要保证这些产品族之间的一致性。如果直接在代码中创建这些对象,会使得代码与具体产品的细节紧密耦合,不利于后续的扩展和维护。
解决方案:
抽象工厂模式提供了一个接口,用于创建一系列相关或相互依赖的对象。通过使用抽象工厂接口及其具体实现,可以将对象的创建与客户端代码分离,从而实现系统的松耦合。抽象工厂模式涉及多个角色:
- 抽象工厂(Abstract Factory):声明了一组用于创建不同产品的抽象方法。具体的工厂类必须实现这些方法来创建具体的产品对象。
- 具体工厂(Concrete Factory):实现抽象工厂接口,负责创建特定种类的产品对象。
- 抽象产品(Abstract Product):定义了产品的通用接口,具体产品必须实现这个接口。
- 具体产品(Concrete Product):实现抽象产品接口,是抽象工厂创建的实际对象。
效果:
抽象工厂模式的使用可以带来以下效果:
- 产品族一致性:抽象工厂确保创建的产品是一组相关的产品族,保证了这些产品之间的一致性。
- 松耦合:客户端代码不需要直接依赖于具体产品,只需要通过抽象工厂接口创建产品,从而降低了代码的耦合度。
- 可扩展性:增加新的产品族或产品变得相对容易,只需要添加新的具体工厂和产品类即可,不需要修改现有代码。
- 限制:抽象工厂模式要求系统中的每个产品族都必须有一个对应的具体工厂,这可能增加了系统的复杂性。
抽象工厂模式适用于需要创建一系列相关产品并保证它们之间一致性的情况,例如图形界面库中的UI元素,不同操作系统下的界面组件等。通过使用抽象工厂模式,可以更好地管理和组织这些产品的创建过程。
代码示例:
// 抽象产品接口:操作系统
interface OperatingSystem {
void run();
}
// 具体产品:Windows操作系统
class WindowsOS implements OperatingSystem {
@Override
public void run() {
System.out.println("Running Windows OS");
}
}
// 具体产品:Linux操作系统
class LinuxOS implements OperatingSystem {
@Override
public void run() {
System.out.println("Running Linux OS");
}
}
// 抽象产品接口:应用程序
interface Application {
void open();
}
// 具体产品:Word应用程序
class WordApplication implements Application {
@Override
public void open() {
System.out.println("Opening Word Application");
}
}
// 具体产品:Excel应用程序
class ExcelApplication implements Application {
@Override
public void open() {
System.out.println("Opening Excel Application");
}
}
// 抽象工厂接口
interface SoftwareFactory {
OperatingSystem createOperatingSystem();
Application createApplication();
}
// 具体工厂:Windows工厂
class WindowsFactory implements SoftwareFactory {
@Override
public OperatingSystem createOperatingSystem() {
return new WindowsOS();
}
@Override
public Application createApplication() {
return new ExcelApplication();
}
}
// 具体工厂:Linux工厂
class LinuxFactory implements SoftwareFactory {
@Override
public OperatingSystem createOperatingSystem() {
return new LinuxOS();
}
@Override
public Application createApplication() {
return new WordApplication();
}
}
// 在这个示例中,抽象工厂模式通过SoftwareFactory接口和其实现类来创建不同类型的操作系统和应用程序。
// 客户端代码可以根据需要选择不同的工厂实例来创建不同的产品组合。
public class Client {
public static void main(String[] args) {
SoftwareFactory windowsFactory = new WindowsFactory();
OperatingSystem windowsOS = windowsFactory.createOperatingSystem();
Application windowsApp = windowsFactory.createApplication();
windowsOS.run();
windowsApp.open();
SoftwareFactory linuxFactory = new LinuxFactory();
OperatingSystem linuxOS = linuxFactory.createOperatingSystem();
Application linuxApp = linuxFactory.createApplication();
linuxOS.run();
linuxApp.open();
}
}
3. 建造者模式(Builder)
问题:
在某些情况下,一个对象的创建过程非常复杂,涉及多个步骤,每个步骤都可能有不同的实现方式。如果将所有创建逻辑放在一个类中,会导致该类变得庞大且难以维护。此外,如果需要创建不同的变体对象,就需要在该类中添加更多的逻辑,使得代码变得混乱。
解决方案:
建造者模式提供了一种将一个复杂对象的构建过程与其表示分离的方法。它将对象的构建过程封装在一个独立的"建造者"类中,由该类负责逐步构建对象。这样,可以根据需要创建不同的建造者来构建不同的对象变体。通常,建造者模式涉及以下角色:
- 产品(Product):表示正在构建的复杂对象。建造者模式的目标是构建这个产品。
- 抽象建造者(Abstract Builder):定义了构建产品的步骤和方法,但没有具体的实现。不同的具体建造者可以实现不同的构建步骤,从而创建不同的产品变体。
- 具体建造者(Concrete Builder):实现了抽象建造者定义的方法,完成了产品的构建过程。每个具体建造者负责构建特定的产品变体。
- 指导者(Director):负责控制建造的过程。它通过将客户端与具体建造者分离,确保产品的构建是按照一定顺序和规则进行的。
效果:
建造者模式的效果包括:
- 分离构建过程和表示:通过建造者模式,可以将复杂对象的构建过程与其最终表示分离,使得构建过程更加清晰可控。
- 支持不同的表示:通过使用不同的具体建造者,可以创建不同的产品表示,而不改变客户端的代码。
- 更好的可扩展性:如果需要添加新的产品变体,只需创建一个新的具体建造者即可,而无需修改已有的代码。
- 隐藏产品的内部结构:客户端只需与抽象建造者和指导者交互,无需关心产品的内部构建细节。
总之,建造者模式适用于需要构建复杂对象,且构建过程涉及多个步骤或变体的情况。通过将构建过程分解为可重用的步骤,建造者模式提供了一种结构化的方法来创建对象。
代码示例:
// 首先,我们定义房屋类 House,它具有多个属性,如地基、结构、屋顶和装修。
class House {
private String foundation;
private String structure;
private String roof;
private String interior;
public void setFoundation(String foundation) {
this.foundation = foundation;
}
public void setStructure(String structure) {
this.structure = structure;
}
public void setRoof(String roof) {
this.roof = roof;
}
public void setInterior(String interior) {
this.interior = interior;
}
@Override
public String toString() {
return "House [foundation=" + foundation + ", structure=" + structure + ", roof=" + roof + ", interior=" + interior + "]";
}
}
// 然后,我们创建一个抽象建造者类 HouseBuilder,它定义了构建房屋的方法。
abstract class HouseBuilder {
protected House house = new House();
public abstract void buildFoundation();
public abstract void buildStructure();
public abstract void buildRoof();
public abstract void buildInterior();
public House getHouse() {
return house;
}
}
// 接下来,我们创建两个具体的建造者类 ConcreteHouseBuilder 和 LuxuryHouseBuilder
// 分别实现了不同类型房屋的构建过程。
// 具体建造者类 - 普通房屋
class ConcreteHouseBuilder extends HouseBuilder {
@Override
public void buildFoundation() {
house.setFoundation("Standard Foundation");
}
@Override
public void buildStructure() {
house.setStructure("Standard Structure");
}
@Override
public void buildRoof() {
house.setRoof("Standard Roof");
}
@Override
public void buildInterior() {
house.setInterior("Standard Interior");
}
}
// 具体建造者类 - 豪华房屋
class LuxuryHouseBuilder extends HouseBuilder {
@Override
public void buildFoundation() {
house.setFoundation("Strong Foundation");
}
@Override
public void buildStructure() {
house.setStructure("Reinforced Structure");
}
@Override
public void buildRoof() {
house.setRoof("Elegant Roof");
}
@Override
public void buildInterior() {
house.setInterior("Luxury Interior");
}
}
// 最后,我们创建指导者类 Director,它协调建造过程并返回构建的房屋对象。
class Director {
private HouseBuilder builder;
public Director(HouseBuilder builder) {
this.builder = builder;
}
public House constructHouse() {
builder.buildFoundation();
builder.buildStructure();
builder.buildRoof();
builder.buildInterior();
return builder.getHouse();
}
}
// 这个示例演示了如何使用建造者模式创建不同类型的房屋,每种房屋类型的建造过程都由相应的具体建造者类负责实现,而指导者类负责协调建造过程。
public class BuilderPatternExample {
public static void main(String[] args) {
HouseBuilder concreteBuilder = new ConcreteHouseBuilder();
Director director1 = new Director(concreteBuilder);
House concreteHouse = director1.constructHouse();
System.out.println("Concrete House: " + concreteHouse);
HouseBuilder luxuryBuilder = new LuxuryHouseBuilder();
Director director2 = new Director(luxuryBuilder);
House luxuryHouse = director2.constructHouse();
System.out.println("Luxury House: " + luxuryHouse);
}
}
4. 原型模式(Prototype)
问题:
在某些情况下,需要创建对象的副本,但复制一个对象的成本可能很高,或者希望避免与对象的具体类耦合。例如,当创建对象的过程较为复杂,或者对象包含大量共享的状态时,使用常规的创建方法可能会导致性能下降。
解决方案:
原型模式的解决方案是通过复制现有对象来创建新对象,而不是从头开始构建。这允许我们以更高效的方式创建新对象,同时避免了与对象类的直接耦合。核心概念是在原型对象的基础上进行克隆,使得新对象具有与原型相同的初始状态。
在原型模式中,通常会有以下几个角色:
- 抽象原型(Prototype):声明克隆方法,作为所有具体原型的基类或接口。
- 具体原型(Concrete Prototype):实现克隆方法