MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java泛型在设计模式中的应用

2023-08-223.4k 阅读

Java泛型基础回顾

在深入探讨Java泛型在设计模式中的应用之前,我们先来回顾一下Java泛型的基础知识。泛型是Java 5.0引入的一项强大特性,它允许我们在定义类、接口和方法时使用类型参数。这使得代码可以适应不同的数据类型,同时提供编译时的类型安全检查。

泛型类

泛型类的定义形式为在类名后使用尖括号 <> 包含类型参数。例如,下面是一个简单的泛型类 Box,用于存储一个任意类型的对象:

public class Box<T> {
    private T item;

    public void set(T item) {
        this.item = item;
    }

    public T get() {
        return item;
    }
}

在上述代码中,T 是类型参数,它可以代表任何引用类型。我们可以通过如下方式使用这个泛型类:

Box<Integer> integerBox = new Box<>();
integerBox.set(10);
Integer value = integerBox.get();

这里我们创建了一个 Box<Integer> 实例,它只能存储 Integer 类型的对象,这保证了类型安全,避免了在运行时出现类型转换异常。

泛型接口

泛型接口的定义方式与泛型类类似。例如,定义一个泛型接口 Printer

public interface Printer<T> {
    void print(T t);
}

然后可以有实现该接口的类:

public class StringPrinter implements Printer<String> {
    @Override
    public void print(String s) {
        System.out.println(s);
    }
}

也可以使用匿名内部类实现:

Printer<Integer> integerPrinter = new Printer<Integer>() {
    @Override
    public void print(Integer i) {
        System.out.println(i);
    }
};

泛型方法

除了在类和接口中使用泛型,我们还可以定义泛型方法。泛型方法的类型参数在方法返回类型之前声明。例如:

public class GenericMethods {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

调用这个泛型方法:

Integer[] intArray = {1, 2, 3};
GenericMethods.printArray(intArray);

这里的 <T> 表示该方法是泛型方法,T 是类型参数,在调用时会根据传入的实际参数类型确定 T 的具体类型。

Java泛型在单例模式中的应用

单例模式是一种常用的设计模式,用于确保一个类在整个应用程序中只有一个实例。传统的单例模式实现如下:

public class Singleton {
    private static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

然而,这种实现方式存在一些问题,比如在多线程环境下可能会创建多个实例。我们可以使用泛型来实现一个更通用的单例模式,同时利用Java的静态内部类特性来保证线程安全。

public class GenericSingleton<T> {
    private GenericSingleton() {
    }

    public static <T> GenericSingleton<T> getInstance(Class<T> clazz) {
        return InnerHolder.INSTANCE;
    }

    private static class InnerHolder<T> {
        private static final GenericSingleton<T> INSTANCE = new GenericSingleton<>();
    }
}

使用方式如下:

GenericSingleton<String> stringSingleton = GenericSingleton.getInstance(String.class);

在这个实现中,我们通过泛型使得单例模式可以应用于不同类型的对象,同时静态内部类 InnerHolder 的特性保证了在类加载时才创建单例实例,从而实现了线程安全。

Java泛型在工厂模式中的应用

工厂模式是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建和使用分离。简单工厂模式是工厂模式的一种基础形式,下面我们来看如何使用泛型来实现简单工厂模式。

简单工厂模式基础实现

假设我们有一个 Shape 接口和两个实现类 CircleRectangle

public interface Shape {
    void draw();
}

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle.");
    }
}

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle.");
    }
}

传统的简单工厂类如下:

public class ShapeFactory {
    public Shape createShape(String shapeType) {
        if ("circle".equalsIgnoreCase(shapeType)) {
            return new Circle();
        } else if ("rectangle".equalsIgnoreCase(shapeType)) {
            return new Rectangle();
        }
        return null;
    }
}

使用方式:

ShapeFactory factory = new ShapeFactory();
Shape circle = factory.createShape("circle");
circle.draw();

使用泛型改进简单工厂模式

通过泛型,我们可以让工厂类更加通用。假设我们有一个泛型工厂类 GenericFactory

import java.util.HashMap;
import java.util.Map;

public class GenericFactory<T> {
    private final Map<Class<T>, Supplier<T>> creators = new HashMap<>();

    public <S extends T> void register(Class<S> type, Supplier<S> creator) {
        creators.put(type, creator);
    }

    public T create(Class<T> type) {
        Supplier<T> creator = creators.get(type);
        if (creator == null) {
            throw new IllegalArgumentException("No creator registered for type " + type);
        }
        return creator.get();
    }
}

使用方式如下:

GenericFactory<Shape> shapeFactory = new GenericFactory<>();
shapeFactory.register(Circle.class, Circle::new);
shapeFactory.register(Rectangle.class, Rectangle::new);

Shape circle = shapeFactory.create(Circle.class);
circle.draw();

Shape rectangle = shapeFactory.create(Rectangle.class);
rectangle.draw();

在这个泛型工厂类中,register 方法用于注册不同类型对象的创建逻辑,create 方法根据传入的类型创建对象。这样的实现更加灵活和通用,适用于多种类型对象的创建场景。

Java泛型在策略模式中的应用

策略模式定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。

策略模式基础实现

假设我们有一个计算两个数之和的策略接口 SumStrategy 以及两个实现类 NormalSumStrategyAdvancedSumStrategy

public interface SumStrategy {
    int sum(int a, int b);
}

public class NormalSumStrategy implements SumStrategy {
    @Override
    public int sum(int a, int b) {
        return a + b;
    }
}

public class AdvancedSumStrategy implements SumStrategy {
    @Override
    public int sum(int a, int b) {
        // 更复杂的求和逻辑
        return a + b + 1;
    }
}

使用策略模式的上下文类 SumContext

public class SumContext {
    private SumStrategy strategy;

    public SumContext(SumStrategy strategy) {
        this.strategy = strategy;
    }

    public int executeSum(int a, int b) {
        return strategy.sum(a, b);
    }
}

使用方式:

SumContext normalContext = new SumContext(new NormalSumStrategy());
int normalSum = normalContext.executeSum(2, 3);

SumContext advancedContext = new SumContext(new AdvancedSumStrategy());
int advancedSum = advancedContext.executeSum(2, 3);

使用泛型改进策略模式

通过泛型,我们可以使策略模式适用于不同类型的计算。例如,定义一个泛型策略接口 GenericCalculationStrategy

public interface GenericCalculationStrategy<T> {
    T calculate(T a, T b);
}

实现类 GenericSumStrategy

import java.math.BigDecimal;

public class GenericSumStrategy implements GenericCalculationStrategy<BigDecimal> {
    @Override
    public BigDecimal calculate(BigDecimal a, BigDecimal b) {
        return a.add(b);
    }
}

泛型上下文类 GenericCalculationContext

public class GenericCalculationContext<T> {
    private GenericCalculationStrategy<T> strategy;

    public GenericCalculationContext(GenericCalculationStrategy<T> strategy) {
        this.strategy = strategy;
    }

    public T executeCalculation(T a, T b) {
        return strategy.calculate(a, b);
    }
}

使用方式:

GenericCalculationStrategy<BigDecimal> sumStrategy = new GenericSumStrategy();
GenericCalculationContext<BigDecimal> context = new GenericCalculationContext<>(sumStrategy);

BigDecimal num1 = new BigDecimal("10.5");
BigDecimal num2 = new BigDecimal("5.5");
BigDecimal result = context.executeCalculation(num1, num2);
System.out.println(result);

通过泛型,策略模式可以应用于更多的数据类型,而不仅仅局限于基本数据类型,增强了代码的复用性和灵活性。

Java泛型在代理模式中的应用

代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。

代理模式基础实现

假设我们有一个 Subject 接口和一个实现类 RealSubject

public interface Subject {
    void request();
}

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject is handling request.");
    }
}

代理类 ProxySubject

public class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject() {
        realSubject = new RealSubject();
    }

    @Override
    public void request() {
        System.out.println("Proxy is pre - processing.");
        realSubject.request();
        System.out.println("Proxy is post - processing.");
    }
}

使用方式:

Subject proxy = new ProxySubject();
proxy.request();

使用泛型改进代理模式

使用泛型,我们可以创建一个更通用的代理类,适用于不同类型的目标对象。利用Java的动态代理机制,结合泛型来实现。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class GenericProxy<T> {
    private T target;

    public GenericProxy(T target) {
        this.target = target;
    }

    public T getProxy() {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("Proxy is pre - processing.");
                        Object result = method.invoke(target, args);
                        System.out.println("Proxy is post - processing.");
                        return result;
                    }
                });
    }
}

使用方式:

Subject realSubject = new RealSubject();
GenericProxy<Subject> proxy = new GenericProxy<>(realSubject);
Subject proxySubject = proxy.getProxy();
proxySubject.request();

在这个实现中,GenericProxy 类使用泛型来处理不同类型的目标对象,通过Java的动态代理机制,在方法调用前后添加通用的处理逻辑,使得代理模式更加通用和灵活。

Java泛型在观察者模式中的应用

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

观察者模式基础实现

假设我们有一个 Subject 接口和一个实现类 ConcreteSubject,以及一个 Observer 接口和一个实现类 ConcreteObserver

import java.util.ArrayList;
import java.util.List;

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

public class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }
}

public interface Observer {
    void update(Subject subject);
}

public class ConcreteObserver implements Observer {
    private int observerState;

    @Override
    public void update(Subject subject) {
        observerState = ((ConcreteSubject) subject).getState();
        System.out.println("Observer state updated to: " + observerState);
    }
}

使用方式:

ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer = new ConcreteObserver();
subject.registerObserver(observer);
subject.setState(10);

使用泛型改进观察者模式

通过泛型,我们可以让观察者模式更加通用,适用于不同类型的主题对象和状态。

import java.util.ArrayList;
import java.util.List;

public class GenericSubject<T> {
    private List<GenericObserver<T>> observers = new ArrayList<>();
    private T state;

    public T getState() {
        return state;
    }

    public void setState(T state) {
        this.state = state;
        notifyObservers();
    }

    public void registerObserver(GenericObserver<T> observer) {
        observers.add(observer);
    }

    public void removeObserver(GenericObserver<T> observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (GenericObserver<T> observer : observers) {
            observer.update(this);
        }
    }
}

public interface GenericObserver<T> {
    void update(GenericSubject<T> subject);
}

public class GenericConcreteObserver<T> implements GenericObserver<T> {
    private T observerState;

    @Override
    public void update(GenericSubject<T> subject) {
        observerState = subject.getState();
        System.out.println("GenericObserver state updated to: " + observerState);
    }
}

使用方式:

GenericSubject<Integer> integerSubject = new GenericSubject<>();
GenericConcreteObserver<Integer> integerObserver = new GenericConcreteObserver<>();
integerSubject.registerObserver(integerObserver);
integerSubject.setState(20);

在这个泛型实现中,GenericSubjectGenericObserver 可以处理不同类型的状态,使得观察者模式更加灵活和可复用,能够适应更多不同类型的观察主题和状态变化场景。

Java泛型在装饰器模式中的应用

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。它通过创建一个包装对象,也就是装饰器,来包裹真实的对象。

装饰器模式基础实现

假设我们有一个 Component 接口和一个实现类 ConcreteComponent,以及一个装饰器抽象类 Decorator 和一个具体装饰器类 ConcreteDecorator

public interface Component {
    void operation();
}

public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent is performing operation.");
    }
}

public abstract class Decorator implements Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}

public class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        System.out.println("ConcreteDecorator is pre - processing.");
        super.operation();
        System.out.println("ConcreteDecorator is post - processing.");
    }
}

使用方式:

Component component = new ConcreteComponent();
Component decoratedComponent = new ConcreteDecorator(component);
decoratedComponent.operation();

使用泛型改进装饰器模式

使用泛型可以让装饰器模式更加通用,适用于不同类型的组件。

public interface GenericComponent<T> {
    T operation();
}

public class GenericConcreteComponent<T> implements GenericComponent<T> {
    private T data;

    public GenericConcreteComponent(T data) {
        this.data = data;
    }

    @Override
    public T operation() {
        System.out.println("GenericConcreteComponent is performing operation.");
        return data;
    }
}

public abstract class GenericDecorator<T> implements GenericComponent<T> {
    protected GenericComponent<T> component;

    public GenericDecorator(GenericComponent<T> component) {
        this.component = component;
    }

    @Override
    public T operation() {
        return component.operation();
    }
}

public class GenericConcreteDecorator<T> extends GenericDecorator<T> {
    public GenericConcreteDecorator(GenericComponent<T> component) {
        super(component);
    }

    @Override
    public T operation() {
        System.out.println("GenericConcreteDecorator is pre - processing.");
        T result = super.operation();
        System.out.println("GenericConcreteDecorator is post - processing.");
        return result;
    }
}

使用方式:

GenericComponent<String> stringComponent = new GenericConcreteComponent<>("Hello");
GenericComponent<String> decoratedStringComponent = new GenericConcreteDecorator<>(stringComponent);
String result = decoratedStringComponent.operation();

通过泛型,装饰器模式可以处理不同类型的组件,提高了代码的复用性和灵活性,能够适应各种不同类型对象的功能装饰需求。

Java泛型在组合模式中的应用

组合模式将对象组合成树形结构以表示“部分 - 整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

组合模式基础实现

假设我们有一个 Component 接口,一个叶子节点类 Leaf 和一个组合节点类 Composite

import java.util.ArrayList;
import java.util.List;

public interface Component {
    void operation();
}

public class Leaf implements Component {
    @Override
    public void operation() {
        System.out.println("Leaf is performing 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 child : children) {
            child.operation();
        }
    }
}

使用方式:

Component leaf1 = new Leaf();
Component leaf2 = new Leaf();

Composite composite = new Composite();
composite.add(leaf1);
composite.add(leaf2);

composite.operation();

使用泛型改进组合模式

通过泛型,我们可以使组合模式更加通用,适用于不同类型的组件和操作。

import java.util.ArrayList;
import java.util.List;

public interface GenericComponent<T> {
    T operation();
}

public class GenericLeaf<T> implements GenericComponent<T> {
    private T data;

    public GenericLeaf(T data) {
        this.data = data;
    }

    @Override
    public T operation() {
        System.out.println("GenericLeaf is performing operation.");
        return data;
    }
}

public class GenericComposite<T> implements GenericComponent<T> {
    private List<GenericComponent<T>> children = new ArrayList<>();

    public void add(GenericComponent<T> component) {
        children.add(component);
    }

    public void remove(GenericComponent<T> component) {
        children.remove(component);
    }

    @Override
    public T operation() {
        T result = null;
        for (GenericComponent<T> child : children) {
            result = child.operation();
        }
        return result;
    }
}

使用方式:

GenericComponent<Integer> integerLeaf1 = new GenericLeaf<>(10);
GenericComponent<Integer> integerLeaf2 = new GenericLeaf<>(20);

GenericComposite<Integer> integerComposite = new GenericComposite<>();
integerComposite.add(integerLeaf1);
integerComposite.add(integerLeaf2);

Integer result = integerComposite.operation();

通过泛型,组合模式可以处理不同类型的组件和操作结果,增强了代码的通用性和灵活性,能够更好地适应不同类型的“部分 - 整体”层次结构管理需求。

总结泛型在设计模式中的优势

  1. 类型安全:泛型在编译时进行类型检查,避免了运行时类型转换异常,提高了代码的稳定性和可靠性。在各种设计模式中,如工厂模式创建对象、策略模式执行不同类型的计算等,确保了对象和数据类型的一致性。
  2. 代码复用:通过泛型,设计模式可以适用于多种不同的数据类型,减少了重复代码。例如在代理模式、装饰器模式和组合模式中,泛型使得这些模式可以处理不同类型的对象,而无需为每种类型单独编写代码。
  3. 灵活性:泛型增强了设计模式的灵活性,使其能够更好地适应不同的业务需求变化。在观察者模式中,泛型可以处理不同类型的主题状态变化通知,满足各种复杂的观察场景。
  4. 可读性:使用泛型使得代码更加清晰,明确了类型参数,增强了代码的可读性和可维护性。例如在泛型工厂模式中,通过类型参数清晰地表明了工厂创建对象的类型。

通过深入理解和应用Java泛型在各种设计模式中的特性,我们能够编写出更加健壮、灵活和可维护的Java程序。