设计模式:深入剖析常见设计模式
什么是设计模式?
设计模式是解决复杂问题的方案。设计模式的核心在于以特定的方式构建类和接口,从而解决特定的设计问题。通常,在系统设计过程中,我们会遇到一些问题,而针对这些问题,我们可以使用一系列设计模式。设计模式通常是包含类、接口以及类之间关系的模板。
设计模式的类型:
创造型设计模式:
这类模式旨在以符合特定情境的方式创建对象。
在创建层面,我们可以确定如何独立创建或组合系统的特定部分,从而确保灵活性和兼容性。
属于此类别的设计模式包括:
- 单例模式:在这种设计模式中,我们只有一个实例,并且该实例将在整个应用程序中使用。
单例设计模式要点:
- 私有构造函数:将构造函数标记为私有非常重要,因为我们要确保只创建一个类的实例。
- 私有静态实例:我们使用私有访问修饰符,因为我们想要确保类内存中只有一个对象实例。
- 公共静态方法(访问器):这是对单个实例的全局访问点。此方法主要用于在实例不存在时创建实例,否则,如果实例已存在,则返回该实例。
单例设计模式示例
public class Singleton {
// Private static instance of the class
private static Singleton instance;
private int count;
// Private constructor to prevent instantiation
private Singleton() {
// initialization code
}
// Public static method to provide access to the instance
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// Example method
public void getCount() {
System.out.println("The value of count is: " + count);
}
public void increaseCount() {
count++;
}
public void decreaseCount() {
count--;
}
}
public class Main {
public static void main(String[] args) {
// Get the single instance of Singleton
Singleton singleton = Singleton.getInstance();
singleton.increaseCount();
singleton.getCount(); // Output: The value of count is: 1
// Get the same instance of Singleton
Singleton anotherSingleton = Singleton.getInstance();
anotherSingleton.decreaseCount();
anotherSingleton.getCount(); // Output: The value of count is: 0
// Both singleton and anotherSingleton refer to the same instance
}
}
- 建造者模式:在建造者模式中,我们定义了一种逐步创建对象的方法。这种模式也提供了灵活性,允许使用相同的构造过程创建同一对象的不同版本。
建造者模式的关键要素:
- 产品:它是正在构建的复杂物体。
- 构建器接口:定义用于创建产品不同部分的方法。这些方法通常返回构建器对象本身,以便进行方法链式调用。
- 具体构建器:实现构建器接口,并提供创建产品部件的具体实现。
建造者模式示例:
本示例展示了如何使用建造者设计模式,通过逐步添加配料来制作巧克力酱面包。
// Product Class
class Bread {
private String bread;
private String spread;
private String chiaSeeds;
private String pumpkinSeeds;
public void setBread(String bread) {
this.bread = bread;
}
public void setSpread(String spread) {
this.spread = spread;
}
public void setChiaSeeds(String chiaSeeds) {
this.chiaSeeds = chiaSeeds;
}
public void setPumpkinSeeds(String pumpkinSeeds) {
this.pumpkinSeeds = pumpkinSeeds;
}
@Override
public String toString() {
return "Bread with " + spread + ", topped with " + chiaSeeds + " and " + pumpkinSeeds;
}
}
// Builder Interface
interface BreadBuilder {
BreadBuilder addBread();
BreadBuilder addChocolateSpread();
BreadBuilder addChiaSeeds();
BreadBuilder addPumpkinSeeds();
Bread build();
}
// Concrete Builder
class ChocolateBreadBuilder implements BreadBuilder {
private Bread bread = new Bread();
@Override
public BreadBuilder addBread() {
bread.setBread("Whole grain bread");
return this;
}
@Override
public BreadBuilder addChocolateSpread() {
bread.setSpread("Chocolate spread");
return this;
}
@Override
public BreadBuilder addChiaSeeds() {
bread.setChiaSeeds("Chia seeds");
return this;
}
@Override
public BreadBuilder addPumpkinSeeds() {
bread.setPumpkinSeeds("Pumpkin seeds");
return this;
}
@Override
public Bread build() {
return bread;
}
}
// Client Code
public class Main {
public static void main(String[] args) {
// Create a builder and build the chocolate spread bread
BreadBuilder builder = new ChocolateBreadBuilder();
Bread myBread = builder.addBread()
.addChocolateSpread()
.addChiaSeeds()
.addPumpkinSeeds()
.build();
// Output the result
System.out.println(myBread);
}
}
- 工厂方法:在工厂方法模式中,我们定义了一种创建对象的方法,但我们允许子类决定要创建的对象的具体类型。
工厂模式的关键要素:
- 产品接口:定义所有产品的通用接口。
- 具体产品:实现产品接口。
- 创建者:声明工厂方法。
- 具体创建器:实现工厂方法以返回不同的具体产品。
// Product Interface
interface Juice {
void serve();
}
// Concrete Product 1
class OrangeJuice implements Juice {
@Override
public void serve() {
System.out.println("Serving Orange Juice.");
}
}
// Concrete Product 2
class MangoJuice implements Juice {
@Override
public void serve() {
System.out.println("Serving Mango Juice.");
}
}
// Creator Abstract Class
abstract class JuiceFactory {
// Factory method
public abstract Juice createJuice();
}
// Concrete Creator 1
class OrangeJuiceFactory extends JuiceFactory {
@Override
public Juice createJuice() {
return new OrangeJuice();
}
}
// Concrete Creator 2
class MangoJuiceFactory extends JuiceFactory {
@Override
public Juice createJuice() {
return new MangoJuice();
}
}
// Client Code
public class Main {
public static void main(String[] args) {
// Create an Orange Juice using its factory
JuiceFactory orangeJuiceFactory = new OrangeJuiceFactory();
Juice orangeJuice = orangeJuiceFactory.createJuice();
orangeJuice.serve(); // Output: Serving Orange Juice.
// Create a Mango Juice using its factory
JuiceFactory mangoJuiceFactory = new MangoJuiceFactory();
Juice mangoJuice = mangoJuiceFactory.createJuice();
mangoJuice.serve(); // Output: Serving Mango Juice.
}
}
结构设计模式
这种设计模式主要关注如何将类和对象组合成更大的结构。它们着重于对象和类之间的组织和关系,旨在简化结构、增强灵活性并提高可维护性。
- 适配器模式:在这种模式下,我们允许接口不兼容的对象协同工作。它充当两个不兼容接口之间的桥梁,使它们能够在不修改现有代码的情况下进行通信。
适配器模式的关键要素:
- 目标接口:它是一个能够解决问题的接口(弥合不兼容接口之间的差距)。
- 客户端:与目标接口交互的类或代码。
- 适配对象:这是与当前客户要求不兼容的接口。
- 适配器:实现目标接口并包含被适配对象的实例。它将来自目标接口的请求转换为被适配对象的接口,使它们兼容。
// Target Interface (Menu)
interface Menu {
void orderDish(String dish);
}
// Adaptee (Chef)
class Chef {
public void prepareDish(String dishName) {
System.out.println("Chef is preparing " + dishName + ".");
}
}
// Adapter (Waiter)
class Waiter implements Menu {
private Chef chef;
public Waiter(Chef chef) {
this.chef = chef;
}
@Override
public void orderDish(String dish) {
chef.prepareDish(dish);
}
}
// Client Code
public class Restaurant {
public static void main(String[] args) {
Chef chef = new Chef();
Menu waiter = new Waiter(chef);
// Customer places an order via the waiter
waiter.orderDish("Spaghetti Carbonara"); // Output: Chef is preparing Spaghetti Carbonara.
}
}
- 外观模式:通过提供统一的接口(外观)简化与复杂系统的交互。客户端无需直接调用各个对象中的多个不同方法,而是直接与外观交互,外观会在内部管理这些操作。
立面设计模式的关键要素:
- 外观:它是一个封装所有复杂子系统接口的接口,并将复杂的任务委托给实际执行工作的子系统。
- 子系统类:这些类实际执行工作。
立面设计模式示例:
该示例展示了立面模式,它简化了衣物洗涤、烘干和熨烫流程。它将与多个子系统交互的复杂性隐藏在一个统一的界面之后。
// Subsystem Classes
class WashingMachine {
public void wash() {
System.out.println("Washing clothes.");
}
}
class Dryer {
public void dry() {
System.out.println("Drying clothes.");
}
}
class Iron {
public void press() {
System.out.println("Pressing clothes.");
}
}
// Facade Class
class LaundryFacade {
private WashingMachine washingMachine;
private Dryer dryer;
private Iron iron;
public LaundryFacade(WashingMachine washingMachine, Dryer dryer, Iron iron) {
this.washingMachine = washingMachine;
this.dryer = dryer;
this.iron = iron;
}
public void doLaundry() {
System.out.println("Starting the laundry process...");
washingMachine.wash();
dryer.dry();
iron.press();
System.out.println("Laundry process complete.");
}
}
// Client Code
public class Main {
public static void main(String[] args) {
WashingMachine washingMachine = new WashingMachine();
Dryer dryer = new Dryer();
Iron iron = new Iron();
LaundryFacade laundryFacade = new LaundryFacade(washingMachine, dryer, iron);
// Use the facade to do the laundry
laundryFacade.doLaundry();
}
}
行为设计模式
这一类别下的模式主要涉及物体之间的通信以及它们如何相互互动。
- 迭代器模式:在迭代器模式中,我们定义了一种无需使用传统方法(例如 for 循环或直接索引)即可顺序访问集合元素的方法。该模式提供了一个标准接口(通常是诸如 `next()` 和 `hasNext()` 之类的方法)来遍历集合。这种方法抽象了迭代过程,允许客户端在无需了解集合内部结构或使用传统迭代方法的情况下浏览集合。
该模式的关键要素包括:
- 迭代器接口:我们定义了诸如 next()、hasNext() 和 currentItem() 等所有方法。这些方法用于遍历集合。
- 具体迭代器:这是迭代器接口的具体实现。
- 聚合接口:在这个接口中,我们定义了创建迭代器的方法。所有方法都返回一个迭代器实例。
- 具体骨料:它只是骨料接口的具体实现。
迭代器模式示例:
此示例演示了使用迭代器模式的简单用例,即创建一个员工对象。
// Iterator Interface
interface Iterator {
boolean hasNext();
Object next();
}
// Aggregate Interface
interface Aggregate {
Iterator createIterator();
}
// Employee Class
class Employee {
public String Name;
public int Age;
public String Department;
public int EmployeeId;
public Employee(String name, int age, String department, int employeeId) {
this.Name = name;
this.Age = age;
this.Department = department;
this.EmployeeId = employeeId;
}
}
// Concrete Aggregate
class EmployeeCollection implements Aggregate {
private Employee[] employees;
public EmployeeCollection(Employee[] employees) {
this.employees = employees;
}
@Override
public Iterator createIterator() {
return new EmployeeIterator(this.employees);
}
}
// Concrete Iterator
class EmployeeIterator implements Iterator {
private Employee[] employees;
private int position = 0;
public EmployeeIterator(Employee[] employees) {
this.employees = employees;
}
@Override
public boolean hasNext() {
return position < employees.length;
}
@Override
public Object next() {
return hasNext() ? employees[position++].Name : null;
}
}
// Client Code
public class Main {
public static void main(String[] args) {
// Creating employee array
Employee[] employees = {
new Employee("John", 28, "Engineering", 101),
new Employee("Jane", 32, "Marketing", 102),
new Employee("Tom", 25, "Sales", 103)
};
// Creating employee collection and iterator
EmployeeCollection employeeCollection = new EmployeeCollection(employees);
Iterator iterator = employeeCollection.createIterator();
// Iterating through employees
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
- 策略模式:在这种模式下,我们定义一个算法族,并在运行时选择使用哪个算法。代码并非直接实现单个算法,而是从算法族中接收运行时指令来决定使用哪个算法。这种模式允许算法根据使用它的客户端而独立变化。
该模式的关键要素包括:
1.策略接口:定义所有受支持算法的通用接口。2
.具体策略:使用特定算法实现策略接口。3
.上下文:使用策略来执行算法。
策略模式示例:
假设我们正在构建一个编码系统,根据具体情况可能需要使用不同的编码算法。我们将使用策略模式来演示这个系统。
// Strategy Interface
interface EncoderStrategy {
void encode(String string);
}
// Concrete Strategy for Base64 Encoding
class Base64Encoder implements EncoderStrategy {
@Override
public void encode(String string) {
// Implement Base64 encoding logic here
System.out.println("This method uses Base64 encoding algorithm for: " + string);
}
}
// Concrete Strategy for MD5 Encoding
class MD5Encoder implements EncoderStrategy {
@Override
public void encode(String string) {
// Implement MD5 encoding logic here
System.out.println("This method uses MD5 encoding algorithm for: " + string);
}
}
// Context Class
class EncoderContext {
private EncoderStrategy strategy;
public void setEncoderMethod(EncoderStrategy strategy) {
this.strategy = strategy;
}
public void encode(String string) {
strategy.encode(string);
}
}
// Usage
public class Main {
public static void main(String[] args) {
EncoderContext context = new EncoderContext();
// Use Base64 encoding method
context.setEncoderMethod(new Base64Encoder());
context.encode("A34937ifdsuhfweiur");
// Use MD5 encoding method
context.setEncoderMethod(new MD5Encoder());
context.encode("89743297dfhksdhWOJO");
}
}
解释:
- 首先,我们定义接口。
- 接下来,我们将创建已定义接口的具体实现。
- 最后,我们利用这些实现来观察主体的变化如何也更新其依赖项。
- 观察者模式:一种行为设计模式,它在对象之间建立一对多的依赖关系。这意味着当一个对象(主体)的状态发生变化时,所有依赖对象(观察者)都会收到通知并自动更新。这种模式对于在事件驱动软件中实现分布式事件处理系统尤为有用。
该模式的关键要素包括:
- 主题:这是一个保存状态并在其状态更新时通知观察者的对象。
- 观察者:一个接口或抽象类,定义了更新方法,当主体的状态发生变化时,将调用该方法。
- 具体主题:实现 Subject 接口并维护观察者感兴趣的状态的类。
- 具体观察者:一个实现了观察者接口并更新其状态以匹配主体状态的类。
观察者模式示例:
在股票交易应用程序中,股票行情显示屏充当主体。每当股票价格更新时,投资者和监管机构等各类观察者都会收到通知。这使得他们能够实时应对价格波动。
import java.util.ArrayList;
import java.util.List;
// Observer interface
interface Observer {
void update(String stockSymbol, double stockPrice);
}
// Subject interface
interface Subject {
void register(Observer o);
void remove(Observer o);
void notify();
}
// Concrete Subject
class Stock implements Subject {
private List<Observer> observers;
private String stockSymbol;
private double stockPrice;
public Stock() {
observers = new ArrayList<>();
}
public void setStock(String stockSymbol, double stockPrice) {
this.stockSymbol = stockSymbol;
this.stockPrice = stockPrice;
notify();
}
@Override
public void register(Observer o) {
observers.add(o);
}
@Override
public void remove(Observer o) {
observers.remove(o);
}
@Override
public void notify() {
for (Observer observer : observers) {
observer.update(stockSymbol, stockPrice);
}
}
}
// Concrete Observer
class StockTrader implements Observer {
private String traderName;
public StockTrader(String traderName) {
this.traderName = traderName;
}
@Override
public void update(String stockSymbol, double stockPrice) {
System.out.println("Trader " + traderName + " notified. Stock: " + stockSymbol + " is now $" + stockPrice);
}
}
// Usage
public class Main {
public static void main(String[] args) {
Stock stock = new Stock();
StockTrader trader1 = new StockTrader("Niharika");
StockTrader trader2 = new StockTrader("Goulikar");
stock.register(trader1);
stock.register(trader2);
stock.setStock("Niha", 9500.00);
stock.setStock("Rika", 2800.00);
}
}
解释:
- 首先,我们定义主体的接口,它负责发送更新。类似地,我们也定义观察者的接口,它负责接收更新。
- 接下来,我们将创建已定义接口的具体实现。
- 最后,我们利用这些实现来观察主体的变化如何也更新其依赖项。