-
单例模式(Singleton Pattern)
-
特点:
单例模式(Singleton Pattern)是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。
单例模式的核心思想是通过私有化类的构造函数,防止外部直接实例化对象,并提供一个静态方法来获取唯一的实例。在第一次调用该方法时,创建一个实例并保存下来,在后续调用时直接返回这个实例。
单例模式常用于需要一个全局共享的对象或资源,例如日志记录器、数据库连接池等。
单例模式包含以下要点:- 将类的构造函数设置为私有,禁止外部直接实例化。
- 提供一个静态方法来获取唯一的实例,在第一次调用时创建实例。
- 使用静态变量保存实例,确保所有调用都返回同一个实例。
-
优点:
- 唯一实例:使用单例模式可以确保一个类只有一个实例,全局唯一。
- 全局访问点:通过单例模式提供的静态方法,任何地方都可以访问到唯一的实例,方便获取和使用。
- 节约资源:由于单例模式只创建一个实例,节约了系统资源,特别在需要频繁创建销毁对象的场景下。
- 避免多次实例化:单例模式可以防止多个实例被同时创建,避免了可能引起冲突的情况。
-
缺点:
- 无法扩展:由于单例模式只能创建一个实例,如果需要扩展功能或者创建多个实例,需要修改现有的代码。
- 违背开闭原则:因为单例模式将对象实例化逻辑封装在内部,对于客户端来说是透明的,所以在需要修改实例化逻辑时,需要修改单例类的代码。
- 破坏封装性:单例模式的实现通常需要将构造方法设置为私有的,这样会破坏类的封装性,使得类的实例化不可控。
- 可能引起线程安全问题:在多线程环境下,如果没有合理的处理措施,可能会引发竞争条件,导致创建多个实例。
-
示例:
public class Singleton { private static Singleton instance; // 私有构造函数,防止外部实例化 private Singleton() { } // 静态方法获取唯一实例 public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } // 其他方法 public void doSomething() { System.out.println("Do something."); } } // 客户端代码 public class SingletonPatternDemo { public static void main(String[] args) { // 获取单例实例 Singleton singleton = Singleton.getInstance(); // 调用实例方法 singleton.doSomething(); } }
在上述代码中,Singleton类通过私有构造函数防止外部直接实例化。提供一个静态方法getInstance()来获取唯一的实例,在第一次调用时创建一个实例,并使用静态变量instance保存下来。
然后,在客户端代码中,通过Singleton.getInstance()方法获取单例实例,并调用相应的方法。
需要注意的是,上述代码是线程不安全的,多线程环境下可能会创建多个实例。可以通过加锁或者使用双重检查锁等方式来保证线程安全性。
-
-
工厂模式(Factory Pattern)
-
特点
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种抽象的方式来创建对象,而不需要直接实例化具体的类。
工厂模式通过定义一个创建对象的接口,让子类决定实例化哪个类来创建对象。这样可以将对象的实例化延迟到子类中进行,从而更加灵活地创建对象,并降低了客户端与具体类之间的耦合度。
工厂模式包含以下角色:- 抽象工厂(Abstract Factory):声明了创建对象的接口,可以是接口或抽象类。
- 具体工厂(Concrete Factory):实现了抽象工厂的方法,负责实例化具体的对象。
- 抽象产品(Abstract Product):定义了产品的通用接口。
- 具体产品(Concrete Product):实现了抽象产品接口的具体类。
-
优点:
- 封装了对象的创建过程:客户端只需关心抽象工厂和抽象产品,无需关心具体的创建细节。
- 降低了代码耦合度:客户端只依赖于抽象工厂和抽象产品,无需直接实例化具体类。
- 可以轻易添加新的产品类:只需要新增具体产品和对应的具体工厂即可。
-
缺点:
- 增加了系统的复杂度:引入了更多的类和接口,增加了代码量和理解难度。
- 不太容易扩展新的工厂类:当需要新增工厂类时,需要修改抽象工厂的代码。
-
示例:
// 抽象产品 public interface Shape { void draw(); } // 具体产品1 public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } // 具体产品2 public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } // 工厂类 public class ShapeFactory { public Shape getShape(String shapeType) { if (shapeType == null) { return null; } if (shapeType.equalsIgnoreCase("RECTANGLE")) { return new Rectangle(); } else if (shapeType.equalsIgnoreCase("CIRCLE")) { return new Circle(); } return null; } } // 客户端代码 public class FactoryPatternDemo { public static void main(String[] args) { ShapeFactory shapeFactory = new ShapeFactory(); // 创建具体产品对象并调用方法 Shape shape1 = shapeFactory.getShape("RECTANGLE"); shape1.draw(); Shape shape2 = shapeFactory.getShape("CIRCLE"); shape2.draw(); } }
在上述代码中,我们首先定义了抽象产品接口Shape,然后实现了具体的产品类Rectangle和Circle。
接下来,我们定义了工厂类ShapeFactory,其中的getShape()方法根据传入的参数来实例化具体的产品对象。
最后,在客户端代码中创建了一个ShapeFactory对象,并使用该工厂对象来获取具体产品对象并调用其方法。
-
-
抽象工厂模式(Abstract Factory Pattern)
-
特点:
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个接口用于创建一系列相关或依赖对象的家族,而无需指定具体的类。
抽象工厂模式通过引入抽象工厂和具体工厂来解决工厂方法模式中只能创建一种产品的问题。它提供了创建多个产品家族的能力,并且这些产品之间有关联或依赖关系。
抽象工厂模式包含以下角色:- 抽象工厂(Abstract Factory):声明了一组创建产品的方法。
- 具体工厂(Concrete Factory):实现了抽象工厂的方法,负责创建具体产品的对象。
- 抽象产品(Abstract Product):定义了产品的通用接口。
- 具体产品(Concrete Product):实现了抽象产品接口的具体类。
-
优点:
- 封装了对象的创建过程:客户端通过抽象工厂来创建产品对象,无需关心具体的创建细节。
- 保持产品家族的一致性:具体工厂负责创建同一产品家族的产品,确保这些产品之间是相互配合使用的。
- 便于扩展:增加新的具体工厂和产品类容易,不影响已有的其他代码。
-
缺点:
增加了系统的复杂度:引入了更多的类和接口,增加了代码量和理解难度。
-
示例:
// 抽象产品A public interface Shape { void draw(); } // 具体产品A1 public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } // 具体产品A2 public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } // 抽象产品B public interface Color { void fill(); } // 具体产品B1 public class Red implements Color { @Override public void fill() { System.out.println("Inside Red::fill() method."); } } // 具体产品B2 public class Blue implements Color { @Override public void fill() { System.out.println("Inside Blue::fill() method."); } } // 抽象工厂 public abstract class AbstractFactory { abstract Shape getShape(String shapeType); abstract Color getColor(String colorType); } // 具体工厂1 public class ConcreteFactory1 extends AbstractFactory { @Override public Shape getShape(String shapeType) { if (shapeType.equalsIgnoreCase("RECTANGLE")) { return new Rectangle(); } else if (shapeType.equalsIgnoreCase("CIRCLE")) { return new Circle(); } return null; } @Override public Color getColor(String colorType) { return null; } } // 具体工厂2 public class ConcreteFactory2 extends AbstractFactory { @Override public Shape getShape(String shapeType) { return null; } @Override public Color getColor(String colorType) { if (colorType.equalsIgnoreCase("RED")) { return new Red(); } else if (colorType.equalsIgnoreCase("BLUE")) { return new Blue(); } return null; } } // 客户端代码 public class AbstractFactoryPatternDemo { public static void main(String[] args) { AbstractFactory factory1 = new ConcreteFactory1(); Shape shape1 = factory1.getShape("RECTANGLE"); shape1.draw(); AbstractFactory factory2 = new ConcreteFactory2(); Color color1 = factory2.getColor("RED"); color1.fill(); } }
-
-
建造者模式(Builder Pattern)
-
特点:
建造者模式(Builder Pattern)是一种创建型设计模式,它通过将对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的核心思想是将一个复杂对象的构建过程分解为多个简单步骤,并将这些步骤按照特定顺序进行组合,最终构建出一个完整的对象。通过使用建造者模式,可以避免在客户端直接构建复杂对象,使得代码更加清晰、可读性更强。
建造者模式包含以下角色:- 产品(Product):表示要构建的复杂对象。
- 抽象建造者(Abstract Builder):定义了构建产品各部分的抽象方法,并提供获取最终产品的方法。
- 具体建造者(Concrete Builder):实现了抽象建造者的方法,负责具体构建每个部分,并返回最终产品。
- 指挥者(Director):调用具体建造者来构建产品,并控制构建的流程。
-
优点:
- 将复杂对象的构建过程与其表示分离,使得构建过程灵活,易于扩展和修改。
- 隐藏了复杂对象的创建细节,使得客户端无需知道具体的构建过程,只需要指导指挥者就可以得到想要的产品。
- 可以构建不同表示的对象,通过调用不同的具体建造者。
-
缺点:
建造者模式增加了代码量,需要定义多个类。对于简单的对象,建造者模式可能会显得过于繁琐。
-
示例:
// 产品类 public class Car { private String brand; private String color; private int numOfSeats; public void setBrand(String brand) { this.brand = brand; } public void setColor(String color) { this.color = color; } public void setNumOfSeats(int numOfSeats) { this.numOfSeats = numOfSeats; } public String getBrand() { return brand; } public String getColor() { return color; } public int getNumOfSeats() { return numOfSeats; } } // 抽象建造者类 public abstract class CarBuilder { protected Car car; public Car getCar() { return car; } public abstract void buildBrand(); public abstract void buildColor(); public abstract void buildNumOfSeats(); } // 具体建造者类 public class SedanCarBuilder extends CarBuilder { @Override public void buildBrand() { car.setBrand("Sedan"); } @Override public void buildColor() { car.setColor("Black"); } @Override public void buildNumOfSeats() { car.setNumOfSeats(5); } } // 指挥者类 public class CarDirector { private CarBuilder carBuilder; public void setCarBuilder(CarBuilder carBuilder) { this.carBuilder = carBuilder; } public Car getCar() { return carBuilder.getCar(); } public void constructCar() { carBuilder.buildBrand(); carBuilder.buildColor(); carBuilder.buildNumOfSeats(); } } // 客户端代码 public class BuilderPatternDemo { public static void main(String[] args) { CarDirector carDirector = new CarDirector(); CarBuilder sedanCarBuilder = new SedanCarBuilder(); carDirector.setCarBuilder(sedanCarBuilder); carDirector.constructCar(); Car car = carDirector.getCar(); System.out.println("Car Brand: " + car.getBrand()); System.out.println("Car Color: " + car.getColor()); System.out.println("Number of Seats: " + car.getNumOfSeats()); } }
-
-
原型模式(Prototype Pattern):
-
特点
原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制现有对象来创建新对象,而不需要通过实例化类来创建。原型模式允许我们通过克隆已有对象来创建新对象,从而避免了昂贵的对象创建过程。
原型模式包含以下角色:- 原型(Prototype):定义一个克隆自身的方法。
- 具体原型(Concrete Prototype):实现原型接口并实现克隆方法,用于复制自身。
- 客户端(Client):使用原型对象进行克隆操作。
-
优点:
- 简化对象的创建过程:通过复制已有对象来创建新对象,避免了昂贵的对象创建过程。
- 提高性能:克隆对象比直接创建对象更快。
- 避免重复创建对象:可以动态添加或删除原型,根据需要克隆多个对象。
-
缺点:
- 如果对象包含循环引用或深层次嵌套,则需要额外处理。
- 每个子类都需要实现克隆方法,增加了代码量和复杂度。
- 下面是一个原型模式的代码示例:
-
示例:
// 原型接口 public abstract class Shape implements Cloneable { private String id; protected String type; public String getType() { return type; } public String getId() { return id; } public void setId(String id) { this.id = id; } public abstract void draw(); @Override public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } } // 具体原型类 public class Circle extends Shape { public Circle() { type = "Circle"; } @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } public class Rectangle extends Shape { public Rectangle() { type = "Rectangle"; } @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } public class Square extends Shape { public Square() { type = "Square"; } @Override public void draw() { System.out.println("Inside Square::draw() method."); } } // 原型缓存类 public class ShapeCache { private static Map<String, Shape> shapeMap = new HashMap<>(); public static Shape getShape(String shapeId) { Shape cachedShape = shapeMap.get(shapeId); return (Shape) cachedShape.clone(); } public static void loadCache() { Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(), circle); Rectangle rectangle = new Rectangle(); rectangle.setId("2"); shapeMap.put(rectangle.getId(), rectangle); Square square = new Square(); square.setId("3"); shapeMap.put(square.getId(), square); } } // 客户端代码 public class PrototypePatternDemo { public static void main(String[] args) { ShapeCache.loadCache(); Shape clonedShape = ShapeCache.getShape("1"); System.out.println("Shape : " + clonedShape.getType()); Shape clonedShape2 = ShapeCache.getShape("2"); System.out.println("Shape : " + clonedShape2.getType()); Shape clonedShape3 = ShapeCache.getShape("3"); System.out.println("Shape : " + clonedShape3.getType()); } }
在上述代码中,我们定义了一个抽象类Shape作为原型,其中实现了clone()方法。然后创建了具体的子类Circle、Rectangle和Square,并实现了各自的draw()方法。ShapeCache类用于缓存这些原型,并提供了获取克隆对象的方法。最后,在PrototypePatternDemo类中演示了如何使用原型模式创建对象的过程。
-
-
适配器模式(Adapter Pattern):
-
特点:
适配器模式(Adapter Pattern)是一种结构型设计模式,它将一个类的接口转换成客户端所需要的另一个接口,从而使得原本不兼容的类能够一起工作。适配器模式通过包装(或继承)已有的类,将其接口转换为目标接口,实现了两个不兼容接口之间的桥梁。
适配器模式包含以下角色:
- 目标接口(Target Interface):定义客户端所需的接口。
- 源接口(Adaptee Interface):需要被适配的接口。
- 适配器(Adapter):将源接口转换为目标接口,实现目标接口,并持有一个源接口的对象。
-
优点:
- 提高了代码的复用性和灵活性:可以让客户端使用同一接口操作不同的对象。
- 可以封装已有的类:适配器将已有的类进行封装,不会对原有代码产生影响。
- 客户端与目标接口解耦:客户端只需要通过目标接口与适配器交互,不需要了解适配器内部的具体实现。
-
缺点:
- 增加了适配器类的数量,增加了系统的复杂度。
- 过多使用适配器可能会导致系统设计问题,应谨慎使用。
-
示例:
// 目标接口 public interface MediaPlayer { void play(String audioType, String fileName); } // 源接口 public interface AdvancedMediaPlayer { void playVlc(String fileName); void playMp4(String fileName); } // 源接口实现类 public class VlcPlayer implements AdvancedMediaPlayer { @Override public void playVlc(String fileName) { System.out.println("Playing vlc file. Name: " + fileName); } @Override public void playMp4(String fileName) { // do nothing } } // 适配器类 public class MediaAdapter implements MediaPlayer { private AdvancedMediaPlayer advancedMediaPlayer; public MediaAdapter(String audioType) { if (audioType.equalsIgnoreCase("vlc")) { advancedMediaPlayer = new VlcPlayer(); } } @Override public void play(String audioType, String fileName) { if (audioType.equalsIgnoreCase("vlc")) { advancedMediaPlayer.playVlc(fileName); } } } // 客户端代码 public class AudioPlayer implements MediaPlayer { private MediaAdapter mediaAdapter; @Override public void play(String audioType, String fileName) { if (audioType.equalsIgnoreCase("vlc")) { mediaAdapter = new MediaAdapter(audioType); mediaAdapter.play(audioType, fileName); } } } public class AdapterPatternDemo { public static void main(String[] args) { AudioPlayer audioPlayer = new AudioPlayer(); audioPlayer.play("vlc", "song1.vlc"); } }
在这个示例中,我们有一个目标接口MediaPlayer和一个源接口AdvancedMediaPlayer。适配器类MediaAdapter实现了目标接口,并持有一个源接口的对象。最后,通过AudioPlayer客户端来调用目标接口的方法,当需要播放vlc文件时,会使用适配器将其转换为源接口进行播放。
这样,我们就可以通过适配器模式实现了在目标接口中调用源接口的功能,使得客户端能够通过同一接口来操作不同的对象。这样的设计提高了代码的灵活性和复用性。
-