今世软件开发中,规划形式已经成为一种标准的编程实践。在Java编程中,规划形式也相同重要。Java规划形式是软件开发中广泛运用的一种编程办法,它能够协助开发人员更快地编写出高效、可靠和可保护的代码。本文将介绍Java中常用的几种规划形式,并解释它们的长处和用途。
Java常用规划形式(二) – ()
单例形式(Singleton Pattern)
单例形式是最常用的规划形式之一。它能够确保在整个运用程序中,某个类只要一个实例存在,并供给一种拜访这个实例的大局拜访点。单例形式在需求约束某些类的实例数量时非常有用。
它一般用于需求大局拜访的资源,如配置文件、日志记录器、数据库衔接等。
运用场景
- 日志记录器 在一个运用程序中,一般会有多个模块或类需求记录日志。为了防止创立多个日志记录器实例,运用单例形式能够确保只要一个日志记录器实例,然后防止重复记录日志并进步运用程序的功用。
- 数据库衔接 在一个运用程序中,假如需求频频地与数据库交互,运用单例形式能够确保只要一个数据库衔接实例,然后削减数据库衔接的数量,进步运用程序的功用。
- 体系配置 在一个运用程序中,一般会有一些大局的配置参数,如数据库衔接字符串、服务器地址、缓存巨细等。运用单例形式能够确保只要一个配置实例,然后方便管理和修正配置参数。
代码完成
懒汉式
public class Singleton {
private static Singleton instance;
private Singleton() {
// 私有结构函数,防止外部实例化
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
静态内部类办法
SingletonHolder
是一个静态内部类,它包含一个静态的INSTANCE
成员变量,用于存储单例方针。在第一次调用getInstance
办法时,静态内部类会被加载,然后创立单例方针。这种办法既兼顾了线程安全又兼顾了推迟加载的需求。
public class Singleton {
private Singleton() {
// 私有结构函数,防止外部实例化
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
饿汉式
饿汉式在类加载时就创立了单例方针,所以不存在线程安全问题。不过,这种办法可能会导致不必要的资源浪费,由于单例方针的创立可能在运用程序启动时就完成了,而有些运用场景中可能并不需求运用单例方针。
public class Singleton {
// 在类加载时就创立单例方针
private static Singleton instance = new Singleton();
// 将结构函数设为私有,制止外部创立实例
private Singleton() {}
// 供给获取单例方针的办法
public static Singleton getInstance() {
return instance;
}
}
双重查看锁
它能够在确保线程安全的一同完成推迟加载
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
枚举办法
运用枚举完成单例形式的长处是,能够防止反射和序列化进犯。由于枚举类型的结构函数是私有的,所以无法运用反射来创立实例;并且枚举类型的实例在序列化和反序列化时会主动处理好,所以也无法经过序列化和反序列化来破坏单例。
public enum Singleton {
INSTANCE;
public void doSomething() {
// TODO: 完成单例方针的功用
}
}
运用小结
-
对线程安全和功用要求较高,能够考虑运用饿汉式或双重查看锁办法完成单例形式。这两种办法都能确保线程安全,并且在大多数情况下功用也比较好。
-
假如你对线程安全要求不是很高,或许希望在第一次拜访时才创立单例方针,能够考虑运用懒汉式或许静态内部类办法。这两种办法都是推迟加载的,只要在需求时才会创立单例方针。懒汉式不是线程安全的,需求经过加锁等办法来确保线程安全;而静态内部类办法则是天然生成线程安全的,不需求额外的处理。
-
希望完成简略、代码少,且不需求考虑线程安全和推迟加载的问题,能够考虑运用枚举办法。这种办法不只代码简略,并且天然生成线程安全、单例方针创立和调用都很方便。
总归,挑选哪种完成办法需求根据详细需求来决议,需求归纳考虑线程安全、功用、代码杂乱度、推迟加载等要素。
工厂形式(Factory Pattern)
工厂形式是一种创立型形式,它能够为开发人员供给一种在不直接实例化方针的情况下创立方针的办法。工厂形式经过供给一个通用的接口和一组完成,来隐藏详细完成的细节,然后降低了代码的耦合度和依赖性。
运用场景
-
方针的创立进程比较杂乱,需求进行封装:假如创立一个方针需求进行杂乱的初始化进程,或许需求从多个地方获取数据才能创立方针,那么运用工厂形式能够将这些进程封装起来,让客户端代码愈加简洁和易于理解。
-
需求动态扩展或修正方针的创立进程:假如需求增加或修正某个方针的创立进程,而又不希望对客户端代码产生影响,那么运用工厂形式能够很方便地完成这个需求。
-
需求统一管理方针的创立:假如需求统一管理方针的创立进程,或许需求对创立的方针进行某些统一的处理,那么运用工厂形式能够很好地完成这个需求。
-
需求根据不同的条件创立不同的方针:假如需求根据不同的条件来创立不同类型的方针,那么运用工厂形式能够很方便地完成这个需求。
代码完成
经过一个工厂类来封装方针的创立进程,客户端只需求告知工厂类需求创立哪种类型的方针即可。将方针的创立进程与客户端代码分离开来,使代码愈加灵敏和易于扩展
// 界说产品接口
public interface Product {
void operation();
}
// 详细产品类A
public class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductA operation.");
}
}
// 详细产品类B
public class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductB operation.");
}
}
// 工厂类
public class SimpleFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ConcreteProductA();
} else if ("B".equals(type)) {
return new ConcreteProductB();
} else {
throw new IllegalArgumentException("Invalid product type.");
}
}
}
客户端能够经过调用SimpleFactory.createProduct
办法来创立不同类型的产品方针
Product productA = SimpleFactory.createProduct("A");
productA.operation(); // 输出 "ConcreteProductA operation."
Product productB = SimpleFactory.createProduct("B");
productB.operation(); // 输出 "ConcreteProductB operation."
运用小结
在Java中,工厂形式广泛运用于各种结构和类库中,例如JDBC中的DataSource
工厂、Spring结构中的Bean工厂、MyBatis结构中的SqlSessionFactory
等等。
观察者形式(Observer Pattern)
观察者形式是一种行为型形式,它界说了方针之间的一种一对多的依赖联系。在这种形式中,一个方针发生变化时,一切依赖于它的方针都会得到告诉并主动更新。观察者形式能够协助开发人员创立可扩展的运用程序,削减方针之间的直接依赖联系。
运用场景
-
事情处理机制:Java中的Swing GUI结构就是根据观察者形式完成的,当用户与组件交互时,组件会向注册的监听器发送事情告诉,以触发相应的事情处理办法。
-
日志记录:Java中的日志体系也是根据观察者形式完成的,当日志发生变化时,它会告诉一切注册的观察者,例如文件输出流、控制台输出流等,然后完成日志的输出和记录。
-
用户界面规划:在Java中,用户界面规划中的许多元素都能够运用观察者形式完成,例如菜单项、按钮、文本框等,当用户与这些元素交互时,它们会向注册的监听器发送事情告诉,以触发相应的事情处理办法。
-
多线程编程:在Java中,观察者形式还能够用于多线程编程中,当一个线程发生了某些变化时,它能够向其他线程发送告诉,以完成线程间的协作和同步。
代码完成
这个示例中,ConcreteSubject
完成了 Subject
接口,它保护了一个 observers
列表,用于保存注册的观察者方针。当被观察者发生变化时,它会遍历观察者列表,调用每个观察者的 update
办法。
ConcreteObserver
完成了 Observer
接口,它能够接收来自被观察者的告诉,并执行相应的操作。
在测验类 ObserverPatternDemo
中,咱们创立了一个详细的被观察者方针 ConcreteSubject
,并注册了两个详细的观察者方针 observer1
和 observer2
。当被观察者发生变化时,它会告诉一切注册的观察者方针,并调用它们的 update
办法。
// 观察者接口
interface Observer {
void update(String message);
}
// 被观察者接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
// 详细的被观察者完成类
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
// 详细的观察者完成类
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
// 测验类
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("Observer1");
Observer observer2 = new ConcreteObserver("Observer2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.notifyObservers("Hello, World!");
}
}
运用小结
观察者形式的长处在于它供给了一种松耦合的办法,让观察者和主题之间的依赖联系变得愈加灵敏,一同也能够使得程序更易于扩展和保护。
观察者形式的运用场景包含:当一个笼统模型有两个方面,其间一个方面依赖于另一个方面时;当一个方针的改动需求一同改动其他方针的时候;当一个方针的改动需求告诉其他方针而又不希望与被告诉方针构成紧耦合联系时。
适配器形式(Adapter Pattern)
适配器形式是一种结构型形式,它能够将一个类的接口转换成客户端所希望的另一种接口。适配器形式能够协助开发人员在不修正现有代码的情况下,将不兼容的类组合在一同。适配器形式包含以下几个组成部分:
- 方针接口(Target Interface):客户端希望的接口。
- 适配器(Adapter):充当两个不兼容接口之间的桥梁,使得它们能够相互通讯。
- 适配者(Adaptee):需求被适配的方针,它的接口与方针接口不兼容。
- 客户端(Client):运用方针接口的方针。
运用场景
-
当需求将一个已有的类或接口与另一个不兼容的类或接口进行协同作业时。
-
当需求对一个已有的类或接口进行修正,以满意客户端的需求时,可是不希望修正该类或接口的源代码。
-
当需求重新运用一个已有的类或接口,可是不能直接运用该类或接口的办法时。
代码完成
在这个示例中,咱们有一个方针接口 Target
和一个不兼容的适配者 Adaptee
,咱们需求创立一个适配器 Adapter
来让它们能够一同作业。
适配器完成了方针接口 Target
,并在结构函数中承受一个适配者方针 Adaptee
,然后在完成方针接口的 request
办法中调用适配者的 specificRequest
办法。
在客户端中,咱们创立了一个适配者方针 adaptee
,并将其传递给适配器的结构函数创立一个适配器方针 adapter
。最终,咱们运用方针接口 Target
中界说的办法 request
来拜访适配器,然后调用适配者的办法。
// 方针接口
interface Target {
void request();
}
// 适配者
class Adaptee {
void specificRequest() {
System.out.println("Adaptee specificRequest.");
}
}
// 适配器
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端
public class AdapterPatternDemo {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request();
}
}
运用小结
适配器形式是一种非常有用的规划形式,在 JDK 中被广泛运用,能够供给共同的接口,比方
- Java IO 流是一个常见的适配器形式的比方。它供给了一组标准的接口来拜访各种类型的数据源,包含文件、网络衔接、内存等等。每个数据源都有自己的接口,可是 Java IO 流能够将这些不同的接口转换为标准的接口,然后供给共同的拜访办法。
- Java Servlet API 也是一个常见的适配器形式的比方。它界说了一组接口来处理 HTTP 请求和呼应,包含 doGet()、doPost()、doPut() 等等。每个 Servlet 都必须完成这些接口,可是用户只需求完成其间的一部分即可。这些 Servlet 之间的适配作业由 Servlet 容器完成。
装修器形式(Decorator Pattern)
装修器形式是一种结构型形式,它能够答应开发人员在不修正现有方针的情况下,动态地增加新功用。装修器形式经过将一个方针包装在另一个方针中来扩展它的行为,然后进步了代码的灵敏性和可重用性。
运用场景
-
当需求在不修正现有方针结构的前提下增加新的功用或特性时,能够运用装修器形式。这样能够坚持原有代码的稳定性和兼容性,一同也能够增加代码的灵敏性和可扩展性。
-
当需求动态地向方针增加或删去功用时,能够运用装修器形式。这样能够在运行时动态地增加或删去功用,而不需求修正现有的代码。
-
当需求为多个方针增加相同的功用时,能够运用装修器形式。这样能够将相同的功用封装在装修器中,以便于复用和管理。
代码完成
该示例代码中,Shape
是一个接口,界说了一个 draw
办法,表明制作图形的操作。Circle
是一个完成 Shape
接口的类,表明一个圆形。
ShapeDecorator
是一个装修器笼统类,完成了 Shape
接口,并包含一个 Shape
类型的变量 decoratedShape
,表明要装修的方针。RedShapeDecorator
是一个详细的装修器类,承继了 ShapeDecorator
类,并完成了 draw
办法,在制作图形时增加了一个赤色的边框。
在 main
办法中,咱们创立了原始方针 Circle
,以及两个装修器方针 RedShapeDecorator
,别离装修了 Circle
和 Rectangle
方针。经过调用 draw
办法,咱们能够看到方针被动态地增加了一个赤色的边框,而不需求修正原有的代码。
// 界说接口
interface Shape {
void draw();
}
// 完成接口
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
// 装修器笼统类
abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
// 详细装修器类
class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
// 测验代码
public class DecoratorPatternDemo {
public static void main(String[] args) {
// 创立原始方针
Shape circle = new Circle();
// 创立装修器方针
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
// 调用办法
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
运用小结
在实践运用中,装修器形式经常用于图形界面(GUI)开发、输入/输出流处理、缓存机制、日志记录等范畴,能够有效地进步程序的可扩展性和可保护性。比方
-
在 Java 中,装修器形式被广泛运用于 Java IO 流中,以供给各种不同的功用,如缓存、紧缩、加密等等。例如,能够运用 BufferedReader 来缓存读取文件的数据,运用 GZIPOutputStream 来紧缩数据,运用 CipherOutputStream 来加密数据等等。
-
Java Swing 组件是一个经典的装修器形式的比方。它答应在运行时动态地向组件增加功用,如边框、布景、文本等等。例如,能够运用 BorderFactory 来向组件增加边框,运用 Color 来设置组件的布景颜色,运用 Font 来设置组件的字体等等。
-
在 Spring 结构中,装修器形式被广泛运用于完成 AOP。AOP经过署理形式和装修器形式完成。JDK 动态署理和 CGLIB 动态署理两种办法完成署理形式,运用装修器形式对方针方针进行包装,然后完成告诉 (Advice) 的织入。例如,能够运用 @Transactional 来增加事务处理的功用,运用 @Cacheable 来增加缓存处理的功用,等等。