设计模式是面向对象编程中的一种解决问题的经验总结。根据它们的目的和范围,设计模式可以分为三类:创建型模式、结构型模式和行为型模式。以下是常见的几类设计模式及其例子:
1. 创建型模式(Creational patterns):这些模式用于创建对象,隐藏创建的细节,并提供更好的灵活性和可复用性。常见的创建型模式包括:
- 工厂方法模式(Factory Method Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 单例模式(Singleton Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
2. 结构型模式(Structural patterns):这些模式用于组合类和对象以形成更大的结构,并提供更好的灵活性和可复用性。常见的结构型模式包括:
- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 装饰器模式(Decorator Pattern)
- 组合模式(Composite Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
3. 行为型模式(Behavioral patterns):这些模式用于管理对象之间的算法、职责和通信,并提供更好的灵活性和可复用性。常见的行为型模式包括:
- 模板方法模式(Template Method Pattern)
- 命令模式(Command Pattern)
- 迭代器模式(Iterator Pattern)
- 观察者模式(Observer Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 解释器模式(Interpreter Pattern)
- 状态模式(State Pattern)
- 策略模式(Strategy Pattern)
- 职责链模式(Chain of Responsibility Pattern)
- 访问者模式(Visitor Pattern)
这里每种模式选取一种进行详细学习说明
工厂方法模式(Factory Method Pattern)
工厂方法模式(Factory Method Pattern)是一种创建型模式,用于创建对象而不必向客户端暴露对象的创建逻辑。在工厂方法模式中,我们定义一个工厂接口,它包含一个用于创建对象的工厂方法。具体的对象创建则由实现了该接口的具体工厂类来完成,每个具体工厂类负责创建特定类型的对象。
以下是一个简单的工厂方法模式的示例:
假设我们有一个披萨店,可以制作不同种类的披萨,如芝士披萨、蛤蜊披萨等。我们可以使用工厂方法模式来创建披萨对象。首先,我们定义披萨类,包含披萨的属性和方法:
public abstract class Pizza {
protected String name;
protected String dough;
protected String sauce;
protected List<String> toppings = new ArrayList<String>();
public void prepare() {
System.out.println("Preparing " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings:");
for (String topping : toppings){
System.out.println(" " + topping);
}
}
public void bake() {
System.out.println("Bake for 25 minutes at 350");
}
public void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
public void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
}
接下来,我们定义一个工厂接口PizzaFactory
,包含一个用于创建披萨对象的方法createPizza()
:
public interface PizzaFactory {
Pizza createPizza(String type);
}
然后,我们创建几个具体工厂类,分别用于创建芝士披萨和蛤蜊披萨:
public class CheesePizzaFactory implements PizzaFactory {
public Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new CheesePizza();
} else {
return null;
}
}
}
public class ClamPizzaFactory implements PizzaFactory {
public Pizza createPizza(String type) {
if (type.equals("clam")) {
return new ClamPizza();
} else {
return null;
}
}
}
最后,我们可以在披萨店中使用这些工厂类创建披萨对象。以下是披萨店的代码示例:
public class PizzaStore {
private PizzaFactory pizzaFactory;
public PizzaStore(PizzaFactory pizzaFactory) {
this.pizzaFactory = pizzaFactory;
}
public Pizza orderPizza(String type) {
Pizza pizza = pizzaFactory.createPizza(type);
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("Sorry, we don't have that type of pizza.");
}
return pizza;
}
}
在这个示例中,我们通过传递具体的工厂类(如CheesePizzaFactory
或ClamPizzaFactory
)来实例化PizzaStore对象。当我们调用orderPizza()
方法时,它将使用工厂类创建披萨对象,并调用披萨对象的方法来准备、烘烤、切割和装箱披萨。
使用工厂方法模式的优点是可以将对象的创建过程和使用过程分离,从而提高代码的可维护性和可扩展性。此外,工厂方法模式还可以隐藏对象的创建细节,使客户端代码更加简洁和易于理解。
装饰器模式(Decorator Pattern)
装饰器模式(Decorator Pattern)是一种结构型模式,用于动态地将责任添加到对象上。装饰器模式可以在不改变对象接口的情况下,为对象添加新的行为。它通过封装原始对象来实现这一点,然后将装饰器对象嵌套在原始对象中,以便在运行时添加新的行为。
以下是一个简单的装饰器模式的示例:
我们有一个基本的饮料类,包含饮料的描述和价格:
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
我们还有两个具体的饮料类,分别是浓缩咖啡和混合咖啡:
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
public double cost() {
return 1.99;
}
}
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
public double cost() {
return .89;
}
}
现在,我们想要给饮料加上调料(如牛奶、摩卡等),同时保持饮料的价格和描述不变。为了实现这个功能,我们可以使用装饰器模式。我们首先创建一个装饰器类,它继承自饮料类,并包含一个指向饮料对象的引用:
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
然后,我们创建具体的调料类,它们继承自装饰器类,并实现getDescription()
和cost()
方法:
public class Milk extends CondimentDecorator {
Beverage beverage;
public Milk(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Milk";
}
public double cost() {
return .10 + beverage.cost();
}
}
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public double cost() {
return .20 + beverage.cost();
}
}
现在,我们可以使用装饰器类和具体的调料类来给饮料添加调料。例如,我们可以创建一个浓缩咖啡,并在其中加入牛奶和摩卡:
Beverage beverage = new Espresso();
beverage = new Milk(beverage);
beverage = new Mocha(beverage);
System.out.println(beverage.getDescription() + " $" + beverage.cost());
在这个示例中,我们首先创建了一个浓缩咖啡对象,然后使用Milk
装饰器类和Mocha
装饰器类来添加牛奶和摩卡调料。最后,我们调用getDescription()
和cost()
方法来输出饮料的描述和价格。
使用装饰器模式的优点是可以将新行为添加到对象上,而不需要修改原始对象或任何其他对象。这样可以保持代码的开闭原则,即封闭修改,开放扩展。此外,装饰器模式还可以通过组合不同的装饰器类来创建多种不同的行为组合,从而提高代码的灵活性和可复用性。
访问者模式(Visitor Pattern)
Visitor方法是一种设计模式,用于处理对象结构中的元素。在这个模式中,我们定义了一个访问者(Visitor)类,它可以访问对象结构中的每个元素,并执行特定的操作。
该模式的基本思想是将元素和操作分离。元素类通常包含许多不同的操作,这些操作不容易扩展或修改。因此,我们将这些操作转移到访问者类中,这样就可以在不修改元素类的情况下添加新的操作。
当我们使用Visitor模式时,我们需要将具体元素传递给访问者,并调用accept()
方法。这样,访问者就可以访问该元素,并执行所需的操作。
假设我们有一个Shape抽象类和它的子类Circle
和Rectangle
,每个子类都有自己的draw()
方法用于绘制形状。我们想要添加一个新的操作,即计算每个形状的面积。为此,我们可以使用Visitor模式:
// Element
public abstract class Shape {
public abstract void accept(ShapeVisitor visitor);
}
// Concrete Elements
public class Circle extends Shape {
public void draw() {
// draw circle
}
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
public class Rectangle extends Shape {
public void draw() {
// draw rectangle
}
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
// Visitor
public interface ShapeVisitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
}
// Concrete Visitor
public class AreaCalculator implements ShapeVisitor {
private double totalArea;
public void visit(Circle circle) {
totalArea += Math.PI * circle.getRadius() * circle.getRadius();
}
public void visit(Rectangle rectangle) {
totalArea += rectangle.getWidth() * rectangle.getHeight();
}
public double getTotalArea() {
return totalArea;
}
}
在这个示例中,我们定义了`Shape`抽象类和它的两个子类`Circle`和`Rectangle`。每个子类都有自己的`draw()`方法用于绘制形状,并实现了`accept()`方法。`ShapeVisitor`是访问者接口,定义了访问不同形状的方法。`AreaCalculator`是具体访问者类,实现了计算每个形状面积的方法。 当我们想要计算每个形状的面积时,我们可以创建一个`AreaCalculator`对象,并将它传递给每个形状的`accept()`方法。这样,`AreaCalculator`就可以访问每个形状,并计算总面积。 使用Visitor模式的优点是可以轻松地添加新的操作,而不需要修改元素类。此外,Visitor模式还可以将操作与元素分离,提高了代码的可维护性和可扩展性。