设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。它是软件工程中常见问题的解决方案的一种描述或模板。设计模式可以提供一种通用的、可重用的解决方案,帮助开发人员解决某类问题时采用一种结构良好且经过验证的方法。
设计模式并不是可以直接转化为代码的东西,而是一种解决问题的思路、一种经验的总结。它在面向对象软件设计中起到指导作用,通过设计模式,开发人员能够更容易地理解代码结构,更快地定位和解决问题。
设计模式的主要目标包括:
提高代码的可重用性: 设计模式可以使开发人员将经过验证的设计思想和方法应用于新的问题,从而提高代码的可重用性。
提高代码的可维护性: 设计模式能够提供一种清晰的结构,使得代码更易于理解和维护。
降低代码的耦合性: 设计模式通过定义良好的接口和抽象类,有助于降低代码组件之间的依赖关系,减少耦合性。
提高代码的可扩展性: 设计模式通过强调松散耦合,使得系统更容易扩展和修改。
一些常见的设计模式包括单例模式、工厂模式、观察者模式、策略模式等。这些模式都有明确定义的结构和角色,开发人员可以根据问题的性质选择适当的设计模式来解决。设计模式是一种在软件开发中非常有用的实践,但也需要根据具体情况慎重选择和应用。
Java 中常用的设计模式有很多,其中一些主要的设计模式包括:
单例模式(Singleton Pattern):
java.lang.Runtime
、java.awt.Desktop
。工厂模式(Factory Pattern):
java.util.Calendar
、java.text.NumberFormat
。抽象工厂模式(Abstract Factory Pattern):
javax.xml.parsers.DocumentBuilderFactory
。建造者模式(Builder Pattern):
java.lang.StringBuilder
、java.nio.ByteBuffer
。原型模式(Prototype Pattern):
java.lang.Object#clone()
。适配器模式(Adapter Pattern):
java.util.Arrays#asList()
、java.io.InputStreamReader
。装饰器模式(Decorator Pattern):
java.io
中的诸多类,如BufferedReader
。观察者模式(Observer Pattern):
java.util.Observer
、java.util.Observable
。策略模式(Strategy Pattern):
java.util.Comparator
、java.util.Collections#sort()
。责任链模式(Chain of Responsibility Pattern):
java.util.logging.Logger#log()
。这些设计模式提供了在不同场景下解决问题的通用解决方案,有助于提高代码的可维护性、可扩展性和重用性。在实际开发中,根据具体的问题和需求选择合适的设计模式是很重要的。
单例模式(Singleton Pattern) 是一种创建型设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式适用于需要全局访问点且只能有一个实例的场景,例如线程池、缓存、日志对象等。
public class Singleton {
private static final Singleton instance = new Singleton();
// 私有构造方法,防止外部实例化
private Singleton() {}
// 全局访问点
public static Singleton getInstance() {
return instance;
}
}
在饿汉式中,实例在类加载时就被创建,因此保证了线程安全。但是可能会导致不必要的资源浪费,因为实例在整个生命周期中都会存在,即使没有被用到。
public class Singleton {
private static Singleton instance;
// 私有构造方法,防止外部实例化
private Singleton() {}
// 全局访问点,使用双重检查锁定来保证线程安全
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
懒汉式在第一次被请求时才创建实例,避免了不必要的资源浪费。但是需要注意,需要使用双重检查锁定(Double-Checked Locking)来保证在多线程环境中的线程安全性。
两个经典例子分别是:
Java中的Runtime
类: Runtime
类的实例是单例的,可以通过Runtime.getRuntime()
方法获取,用于与运行时环境交互。
Runtime runtime = Runtime.getRuntime();
日志系统中的Logger
类: 许多日志框架使用单例模式来管理日志实例,以确保所有的日志输出都通过同一个日志对象进行管理。
Logger logger = Logger.getLogger("MyLogger");
这两个例子都体现了单例模式在实际应用中的常见场景和优势。
工厂模式(Factory Pattern) 是一种创建型设计模式,其主要目的是提供一个接口来创建对象,但允许子类在运行时更改创建的类。工厂模式解决了直接在代码中使用构造函数创建对象的问题,使得代码更具灵活性和可维护性。
public interface Product {
void produce();
}
public class ConcreteProductA implements Product {
@Override
public void produce() {
System.out.println("Product A is produced.");
}
}
public class ConcreteProductB implements Product {
@Override
public void produce() {
System.out.println("Product B is produced.");
}
}
public class SimpleFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ConcreteProductA();
} else if ("B".equals(type)) {
return new ConcreteProductB();
}
throw new IllegalArgumentException("Invalid product type");
}
}
在简单工厂模式中,通过一个工厂类的静态方法来创建产品对象。这种方式简单易懂,但不符合开闭原则,如果需要新增产品类型,需要修改工厂类的代码。
public interface Product {
void produce();
}
public class ConcreteProductA implements Product {
@Override
public void produce() {
System.out.println("Product A is produced.");
}
}
public class ConcreteProductB implements Product {
@Override
public void produce() {
System.out.println("Product B is produced.");
}
}
public interface Factory {
Product createProduct();
}
public class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
public class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
在工厂方法模式中,定义一个抽象的工厂接口,由具体的工厂类实现,每个工厂类负责创建特定类型的产品。这样可以满足开闭原则,添加新的产品类型时只需新增相应的工厂类。
两个经典的例子是:
Java中的Calendar
类: Calendar
类是一个抽象工厂类,提供了静态方法getInstance()
,根据不同的实现子类(如GregorianCalendar
)来创建不同的日历对象。
Calendar calendar = Calendar.getInstance();
Java中的Executor
框架: Executor
框架提供了一系列工厂方法,用于创建不同类型的线程池,如Executors.newFixedThreadPool()
和Executors.newCachedThreadPool()
等。
ExecutorService executorService = Executors.newFixedThreadPool(5);
这两个例子展示了工厂模式在实际应用中的灵活性和可扩展性,通过工厂模式可以更好地组织和管理对象的创建过程。
抽象工厂模式(Abstract Factory Pattern) 是一种创建型设计模式,与工厂方法模式类似,它也是用于创建一系列相关或相互依赖的对象,但抽象工厂模式更强调一系列相关的产品对象的创建,形成一个产品族,而不仅仅是一个单一的产品。
// 抽象产品A
public interface ProductA {
void useA();
}
// 具体产品A1
public class ConcreteProductA1 implements ProductA {
@Override
public void useA() {
System.out.println("Product A1 is used.");
}
}
// 具体产品A2
public class ConcreteProductA2 implements ProductA {
@Override
public void useA() {
System.out.println("Product A2 is used.");
}
}
// 抽象产品B
public interface ProductB {
void useB();
}
// 具体产品B1
public class ConcreteProductB1 implements ProductB {
@Override
public void useB() {
System.out.println("Product B1 is used.");
}
}
// 具体产品B2
public class ConcreteProductB2 implements ProductB {
@Override
public void useB() {
System.out.println("Product B2 is used.");
}
}
// 抽象工厂
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// 具体工厂1
public class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂2
public class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
在抽象工厂模式中,抽象工厂接口定义了一系列创建相关产品的方法,每个具体工厂类实现了这些方法,分别创建一系列具体的产品对象。这样,客户端可以通过选择具体的工厂来创建一整套相关的产品。
两个经典的例子是:
图形界面库中的抽象工厂: 在图形界面库中,抽象工厂可以定义创建按钮、文本框等界面元素的方法,而具体工厂可以分别实现这些方法以创建特定风格(如Windows风格、Mac风格)的界面元素。
数据库访问库中的抽象工厂: 在数据库访问库中,抽象工厂可以定义创建连接、命令等数据库访问对象的方法,而具体工厂可以实现这些方法以创建特定类型(如MySQL、Oracle)的数据库访问对象。
这两个例子展示了抽象工厂模式在实际应用中的灵活性和可维护性,通过抽象工厂模式可以方便地扩展产品系列,而不影响客户端代码。
建造者模式(Builder Pattern) 是一种创建型设计模式,其主要目的是将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。建造者模式适用于需要一步一步构建复杂对象的场景,以及在构建过程中需要更改对象的属性。
// 产品
public class Product {
private String partA;
private String partB;
private String partC;
// 省略构造函数、getter和setter方法
@Override
public String toString() {
return "Product{partA='" + partA + "', partB='" + partB + "', partC='" + partC + "'}";
}
}
// 抽象建造者
public interface Builder {
void buildPartA();
void buildPartB();
void buildPartC();
Product getResult();
}
// 具体建造者A
public class ConcreteBuilderA implements Builder {
private Product product = new Product();
@Override
public void buildPartA() {
product.setPartA("PartA for BuilderA");
}
@Override
public void buildPartB() {
product.setPartB("PartB for BuilderA");
}
@Override
public void buildPartC() {
product.setPartC("PartC for BuilderA");
}
@Override
public Product getResult() {
return product;
}
}
// 具体建造者B
public class ConcreteBuilderB implements Builder {
private Product product = new Product();
@Override
public void buildPartA() {
product.setPartA("PartA for BuilderB");
}
@Override
public void buildPartB() {
product.setPartB("PartB for BuilderB");
}
@Override
public void buildPartC() {
product.setPartC("PartC for BuilderB");
}
@Override
public Product getResult() {
return product;
}
}
// 指挥者
public class Director {
public void construct(Builder builder) {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
}
}
两个经典的例子就是:
StringBuilder类: StringBuilder
类允许动态地构建字符串对象,通过链式调用append
方法可以方便地添加字符、字符串等内容。最后通过toString
方法获取最终的字符串对象。
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Hello");
stringBuilder.append(" ");
stringBuilder.append("Builder");
String result = stringBuilder.toString();
Java中的Locale.Builder
类: Locale.Builder
类用于构建Locale
对象,可以通过设置语言、国家、地区等属性来构建一个Locale
对象。
Locale.Builder localeBuilder = new Locale.Builder();
Locale locale = localeBuilder.setLanguage("en").setRegion("US").build();
这两个例子展示了建造者模式在实际应用中的灵活性,通过建造者模式,可以更好地组织和管理对象的构建过程,使得代码更加清晰和可维护。
原型模式(Prototype Pattern) 是一种创建型设计模式,其主要目的是通过复制现有对象来创建新对象,而不是通过构造函数进行创建。原型模式对于创建成本较高的对象,或者对象的创建过程比较复杂而又需要频繁创建时,能够提高性能和简化代码。
// 原型接口
public interface Prototype extends Cloneable {
Prototype clone();
}
// 具体原型类
public class ConcretePrototype implements Prototype {
private String attribute;
public ConcretePrototype(String attribute) {
this.attribute = attribute;
}
@Override
public Prototype clone() {
try {
// 利用Java的clone()方法实现对象的浅复制
return (ConcretePrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public String getAttribute() {
return attribute;
}
public void setAttribute(String attribute) {
this.attribute = attribute;
}
}
两个经典的例子是:
Java中的Object.clone()
方法: Object
类提供了一个clone()
方法,可以实现对象的浅复制。任何类只要实现Cloneable
接口并覆写clone()
方法,就可以通过clone()
方法实现对象的复制。
MyClass original = new MyClass();
MyClass copy = (MyClass) original.clone();
图形编辑器中的复制粘贴功能: 在图形编辑器中,用户可以通过复制一个图形对象来创建一个新的相似对象,然后粘贴到画布上。这个过程可以使用原型模式来实现,复制操作就是通过原型对象创建新对象的过程。
这两个例子展示了原型模式在实际应用中的灵活性和可用性,通过原型模式可以方便地实现对象的复制,避免了重复的初始化过程。需要注意的是,对于复杂对象,可能需要考虑深复制的问题,以确保克隆对象的属性也是独立的。
适配器模式(Adapter Pattern) 是一种结构型设计模式,它允许接口不兼容的类能够一起工作。适配器模式充当两个不兼容接口之间的桥梁,使得它们能够协同工作。
// 目标接口
public interface Target {
void request();
}
// 被适配者
public class Adaptee {
public void specificRequest() {
System.out.println("Specific request");
}
}
// 适配器
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
// 目标接口
public interface Target {
void request();
}
// 被适配者
public class Adaptee {
public void specificRequest() {
System.out.println("Specific request");
}
}
// 适配器
public class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
两个经典的例子就是:
Java中的Arrays.asList()
方法: 该方法返回一个List
对象,但底层实际上是基于数组的,通过适配器模式将数组适配为List
。
String[] array = {"A", "B", "C"};
List<String> list = Arrays.asList(array);
Java中的InputStreamReader
类: InputStreamReader
是字节流到字符流的适配器,它将字节流适配为字符流,使得可以按字符而不是字节来读取数据。
InputStream inputStream = new FileInputStream("file.txt");
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
这两个例子都展示了适配器模式在实际应用中的灵活性和可用性,通过适配器模式可以使得原本不兼容的接口能够协同工作。
装饰器模式(Decorator Pattern) 是一种结构型设计模式,它允许动态地将责任附加到对象上。装饰器模式提供了一种灵活的方式,通过扩展类的功能来创建新的对象,而不是通过创建子类。
// 组件接口
public interface Component {
void operation();
}
// 具体组件
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// 装饰器抽象类
public abstract class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器A
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorA operation");
}
}
// 具体装饰器B
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorB operation");
}
}
两个经典的例子如下:
Java中的BufferedReader
类: BufferedReader
是Reader
的装饰器,它通过持有一个Reader
对象,并在其基础上添加了缓冲区的功能,提供了更高效的读取方法。
Reader reader = new FileReader("file.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
Java中的InputStreamReader
类: InputStreamReader
是字节流到字符流的适配器,同时也是Reader
的装饰器,它可以通过包装其他InputStream
对象来提供字符流的读取能力。
InputStream inputStream = new FileInputStream("file.txt");
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
这两个例子都展示了装饰器模式在实际应用中的灵活性和可用性,通过装饰器模式可以在运行时动态地给对象添加额外的功能,而无需修改其结构。
观察者模式(Observer Pattern) 是一种行为型设计模式,其主要目的是定义对象之间的一对多依赖关系,使得当一个对象状态改变时,其所有依赖对象都会得到通知并自动更新。观察者模式提供了一种简单而灵活的方法来实现发布-订阅机制。
// 主题接口
public interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题
public class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
public void setState(int state) {
this.state = state;
notifyObservers();
}
}
// 观察者接口
public interface Observer {
void update(int state);
}
// 具体观察者A
public class ConcreteObserverA implements Observer {
@Override
public void update(int state) {
System.out.println("ConcreteObserverA is notified with state: " + state);
}
}
// 具体观察者B
public class ConcreteObserverB implements Observer {
@Override
public void update(int state) {
System.out.println("ConcreteObserverB is notified with state: " + state);
}
}
两个经典的例子如下:
Java中的java.util.Observable
和java.util.Observer
: Observable
是抽象主题类,Observer
是抽象观察者接口。具体主题通过继承Observable
,具体观察者通过实现Observer
接口。
// 具体主题
public class ConcreteSubject extends Observable {
private int state;
public void setState(int state) {
this.state = state;
setChanged();
notifyObservers(state);
}
}
// 具体观察者
public class ConcreteObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
int state = (int) arg;
System.out.println("ConcreteObserver is notified with state: " + state);
}
}
Android中的事件监听机制: Android中的事件监听机制也是观察者模式的一种应用,例如,通过setOnClickListener
方法设置按钮的点击事件监听器,当按钮被点击时,注册的监听器就会收到通知并执行相应的操作。
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 按钮被点击时执行的操作
}
});
这两个例子展示了观察者模式在实际应用中的灵活性和可用性,通过观察者模式可以实现对象之间的松耦合,使得它们之间的关系更加灵活和可维护。
策略模式(Strategy Pattern) 是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式使得算法的变化独立于使用算法的客户端,从而实现了算法的灵活性和可维护性。
// 策略接口
public interface Strategy {
void performAlgorithm();
}
// 具体策略类A
public class ConcreteStrategyA implements Strategy {
@Override
public void performAlgorithm() {
System.out.println("ConcreteStrategyA is used.");
}
}
// 具体策略类B
public class ConcreteStrategyB implements Strategy {
@Override
public void performAlgorithm() {
System.out.println("ConcreteStrategyB is used.");
}
}
// 上下文
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeAlgorithm() {
strategy.performAlgorithm();
}
}
两个经典的例子如下:
排序算法的策略模式: 假设有一个排序类,可以根据不同的排序算法进行排序。通过策略模式,可以将各种排序算法封装成具体的策略类,然后在运行时选择不同的策略。
// 策略接口
public interface SortingStrategy {
void sort(int[] array);
}
// 具体策略类A
public class BubbleSort implements SortingStrategy {
@Override
public void sort(int[] array) {
// 冒泡排序算法的具体实现
}
}
// 具体策略类B
public class QuickSort implements SortingStrategy {
@Override
public void sort(int[] array) {
// 快速排序算法的具体实现
}
}
// 上下文
public class Sorter {
private SortingStrategy strategy;
public Sorter(SortingStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(SortingStrategy strategy) {
this.strategy = strategy;
}
public void performSort(int[] array) {
strategy.sort(array);
}
}
支付方式的策略模式: 假设有一个支付类,可以根据不同的支付方式进行支付。通过策略模式,可以将各种支付方式封装成具体的策略类,然后在运行时选择不同的策略。
// 策略接口
public interface PaymentStrategy {
void pay(double amount);
}
// 具体策略类A
public class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
// 信用卡支付的具体实现
}
}
// 具体策略类B
public class PayPalPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
// PayPal支付的具体实现
}
}
// 上下文
public class PaymentProcessor {
private PaymentStrategy strategy;
public PaymentProcessor(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void processPayment(double amount) {
strategy.pay(amount);
}
}
这两个例子展示了策略模式在实际应用中的灵活性和可用性,通过策略模式可以方便地扩展和替换算法或业务规则。
责任链模式(Chain of Responsibility Pattern) 是一种行为型设计模式,它允许多个对象都有机会处理请求,从而避免将请求的发送者与接收者直接耦合在一起。请求沿着链传递,直到有一个对象处理它为止。
// 处理者接口
public interface Handler {
void handleRequest(Request request);
}
// 具体处理者A
public class ConcreteHandlerA implements Handler {
private Handler successor;
@Override
public void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE_A) {
// 处理请求的逻辑
} else if (successor != null) {
successor.handleRequest(request);
}
}
public void setSuccessor(Handler successor) {
this.successor = successor;
}
}
// 具体处理者B
public class ConcreteHandlerB implements Handler {
private Handler successor;
@Override
public void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE_B) {
// 处理请求的逻辑
} else if (successor != null) {
successor.handleRequest(request);
}
}
public void setSuccessor(Handler successor) {
this.successor = successor;
}
}
// 请求类
public class Request {
private RequestType type;
public Request(RequestType type) {
this.type = type;
}
public RequestType getType() {
return type;
}
}
// 请求类型枚举
public enum RequestType {
TYPE_A, TYPE_B
}
两个经典的例子:
Java中的异常处理: Java中的异常处理机制就是一个责任链模式的实现。当一个方法抛出异常时,系统会从当前方法开始,按照调用层次逐级向上查找能够处理该异常的catch
块,直到找到合适的处理者或者到达顶层方法。
try {
// 可能抛出异常的代码
} catch (ExceptionType1 e) {
// 处理ExceptionType1的逻辑
} catch (ExceptionType2 e) {
// 处理ExceptionType2的逻辑
} catch (Exception e) {
// 处理其他异常的逻辑
}
Android中的事件分发机制: Android中的事件分发机制也是一个责任链模式的实现。事件从顶层的View
开始传递,依次经过父容器和子View,最终到达目标View。每个View都有机会处理事件,如果处理不了就交给下一个处理者。
public boolean dispatchTouchEvent(MotionEvent event) {
// 一系列的处理逻辑
if (onInterceptTouchEvent(event)) {
// 如果拦截了事件,直接返回,不再往下传递
return true;
} else {
// 没有拦截,继续往下传递
return super.dispatchTouchEvent(event);
}
}
这两个例子都展示了责任链模式在实际应用中的灵活性和可用性,通过责任链模式可以实现一种松散耦合的处理机制,使得系统更易于扩展和维护。
十种主要模式大概如此,但是日常项目中并非经常涉及,但是,可以不用,我们却不能不了解!说不定哪一天的面试就会用到呢!