Java设计模式是一组在解决软件设计中常见问题时的经验总结,它们是可重用的解决方案模板,旨在提高代码的可维护性、灵活性和可扩展性。
一、设计模式可以分为三大类
1、创建型模式(Creational Patterns)
1.1、工厂方法模式(Factory Method)
定义一个创建对象的接口,让子类决定实例化哪一个类。
interface Shape {
void draw();
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
class ShapeFactory {
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
1.2、抽象工厂模式(Abstract Factory)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。
//假设我们有一个系统需要创建不同类型的GUI组件(按钮和文本框),并且这些组件有多种风格(Windows风格和MacOS风格)。
//首先,定义两个接口,分别代表按钮和文本框:
// GUI组件接口
interface Button {
void paint();
}
interface TextBox {
void render();
}
//然后,为每种风格实现这些接口:
// Windows风格的组件
class WindowsButton implements Button {
@Override
public void paint() {
System.out.println("Rendering a Windows button.");
}
}
class WindowsTextBox implements TextBox {
@Override
public void render() {
System.out.println("Rendering a Windows text box.");
}
}
// MacOS风格的组件
class MacOSButton implements Button {
@Override
public void paint() {
System.out.println("Rendering a MacOS button.");
}
}
class MacOSTextBox implements TextBox {
@Override
public void render() {
System.out.println("Rendering a MacOS text box.");
}
}
//接下来,定义抽象工厂接口和具体工厂类:
// 抽象工厂接口
interface GUIFactory {
Button createButton();
TextBox createTextBox();
}
// Windows风格的工厂
class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public TextBox createTextBox() {
return new WindowsTextBox();
}
}
// MacOS风格的工厂
class MacOSFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacOSButton();
}
@Override
public TextBox createTextBox() {
return new MacOSTextBox();
}
}
//最后,客户端代码可以根据需要选择工厂来创建组件:
public class Client {
public static void main(String[] args) {
// 根据操作系统选择工厂
GUIFactory factory;
if (System.getProperty("os.name").contains("Windows")) {
factory = new WindowsFactory();
} else {
factory = new MacOSFactory();
}
// 创建组件并渲染
Button button = factory.createButton();
button.paint();
TextBox textBox = factory.createTextBox();
textBox.render();
}
}
/**
在这个例子中,GUIFactory是一个抽象工厂,它声明了创建GUI组件的接口,而WindowsFactory和MacOSFactory是具体工厂,它们实现了这些接口,用于创建特定风格的组件。客户端代码通过选择合适的工厂来获取与平台相匹配的组件实例,而无需直接关心具体类的创建细节。
**/
1.3、单例模式(Singleton)
保证一个类只有一个实例,并提供一个全局访问点。
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;
}
}
1.4、建造者模式(Builder)
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
/**
建造者模式(Builder Pattern)用于创建复杂的对象,它将对象的构造过程和表示分离,使得构造过程可以有不同的表示。下面是一个简单的Java建造者模式示例,以创建一个汉堡包(Hamburger)为例:
首先,我们定义一个Hamburger类,它代表最终要构建的产品:
**/
public class Hamburger {
private String bread;
private String patty;
private String sauce;
private String cheese;
public Hamburger(String bread, String patty, String sauce, String cheese) {
this.bread = bread;
this.patty = patty;
this.sauce = sauce;
this.cheese = cheese;
}
@Override
public String toString() {
return "Hamburger{" +
"bread='" + bread + '\'' +
", patty='" + patty + '\'' +
", sauce='" + sauce + '\'' +
", cheese='" + cheese + '\'' +
'}';
}
}
//接着,定义一个HamburgerBuilder接口,它声明了构建汉堡的各个步骤:
public interface HamburgerBuilder {
HamburgerBuilder withBread(String bread);
HamburgerBuilder withPatty(String patty);
HamburgerBuilder withSauce(String sauce);
HamburgerBuilder withCheese(String cheese);
Hamburger build();
}
//现在,我们可以创建一个具体的VeggieHamburgerBuilder类,实现HamburgerBuilder接口:
public class VeggieHamburgerBuilder implements HamburgerBuilder {
private String bread;
private String patty;
private String sauce;
private String cheese;
@Override
public HamburgerBuilder withBread(String bread) {
this.bread = bread;
return this;
}
@Override
public HamburgerBuilder withPatty(String patty) {
this.patty = patty;
return this;
}
@Override
public HamburgerBuilder withSauce(String sauce) {
this.sauce = sauce;
return this;
}
@Override
public HamburgerBuilder withCheese(String cheese) {
this.cheese = cheese;
return this;
}
@Override
public Hamburger build() {
return new Hamburger(bread, patty, sauce, cheese);
}
}
//最后,我们可以创建一个Director类来指导如何使用建造者
public class Director {
public Hamburger construct(HamburgerBuilder builder) {
return builder
.withBread("Whole Wheat")
.withPatty("Vegetable Patty")
.withSauce("Tomato Sauce")
.withCheese("No Cheese") // Vegetarian option
.build();
}
}
//客户端代码可以这样使用上述类:
public class Client {
public static void main(String[] args) {
HamburgerBuilder builder = new VeggieHamburgerBuilder();
Director director = new Director();
Hamburger veggieHamburger = director.construct(builder);
System.out.println(veggieHamburger);
}
}
/**
在这个例子中,Director类负责组织建造者的构建步骤,而VeggieHamburgerBuilder则负责具体构建Hamburger对象。这种方式允许我们在不修改Director或Hamburger的情况下,添加新的汉堡类型(如ChickenHamburgerBuilder)。
**/
1.5、原型模式(Prototype)
用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。
/**
定义原型接口(可选)
定义一个原型接口来规范所有的原型对象都必须实现clone()方法。
**/
public interface Prototype {
Prototype clone();
}
/**
实现原型类
接下来,创建一个实现了Cloneable接口并实现了Prototype接口的具体原型类。
这里以一个简单的Shape类为例,假设我们有圆形、方形等形状,它们都可以被复制。
**/
import java.util.Date;
public class Shape implements Cloneable, Prototype {
private String id;
private Date createdOn;
public Shape() {
this.createdOn = new Date();
}
// Getter and Setter methods for id and createdOn
@Override
public Prototype clone() {
try {
return (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError("Clone should be supported", e);
}
}
}
// 具体的形状类,如 Circle 和 Square,继承自 Shape 类并添加各自的属性和方法
class Circle extends Shape {
private int radius;
// Constructor, getters, and setters for radius
}
class Square extends Shape {
private int side;
// Constructor, getters, and setters for side
}
/**
客户端代码
客户端代码使用原型对象的clone()方法来创建新的对象实例。
**/
public class Client {
public static void main(String[] args) {
Circle circle = new Circle();
circle.setId("1");
circle.setRadius(5);
Circle clonedCircle = (Circle) circle.clone();
System.out.println("Original Circle ID: " + circle.getId());
System.out.println("Cloned Circle ID: " + clonedCircle.getId());
// 注意:尽管ID相同,但它们是两个不同的对象,createdOn时间也会不同
}
}
2、结构型模式(Structural Patterns)
2.1、适配器模式(Adapter)
将一个类的接口转换成客户希望的另一个接口,使得原本不兼容的类可以一起工作。
这种模式涉及到一个单一的类,该类负责加入独立接口到另一个接口中,使得两者能够协同工作。
下面是一个简单的Java适配器模式示例:
/**
*** 其中有一个音频播放器只能播放.mp3文件,但希望它也能播放.wav文件。
*** 为此,创建一个适配器来让.wav文件看起来像.mp3文件。
**/
//目标接口(Target Interface)
// 客户端已经期待的接口,即播放器能识别的音频格式
interface MediaPlayer {
void play(String audioType, String fileName);
}
//适配者类(Adaptee Class)
// 现有的音频播放类,只能播放.wav文件
class AudioPlayer implements MediaPlayer {
@Override
public void play(String audioType, String fileName) {
if ("mp3".equalsIgnoreCase(audioType)) {
System.out.println("AudioPlayer cannot play this type of file: " + audioType);
} else if ("wav".equalsIgnoreCase(audioType)) {
System.out.println("Playing wav file. Name: " + fileName);
}
}
}
//适配器类(Adapter Class)
// 适配器类,使AudioPlayer能够播放.mp3文件
class MediaAdapter implements MediaPlayer {
private final AudioPlayer audioPlayer;
public MediaAdapter(AudioPlayer audioPlayer) {
this.audioPlayer = audioPlayer;
}
@Override
public void play(String audioType, String fileName) {
if ("mp3".equalsIgnoreCase(audioType)) {
// 将.mp3文件转换为.wav格式,然后调用AudioPlayer的play方法
fileName = fileName.replace(".mp3", ".wav");
audioPlayer.play("wav", fileName);
} else {
audioPlayer.play(audioType, fileName);
}
}
}
//客户端代码(Client Code)
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
// 使用适配器播放.mp3文件
MediaPlayer mediaAdapter = new MediaAdapter(audioPlayer);
mediaAdapter.play("mp3", "song.mp3");