常见设计模式总结

参考文章

  1. 网站计数器
  2. 日志应用
  3. 配置对象类
  4. 数据库连接池
  5. 生成唯一序列号

    实现方法

    1. 饿汉式
    1. public class SingleObject{
    2. private static SingleObject instance = new SingleObject();
    3. // 私有化构造方法,使外部无法直接new获得对象
    4. private SingleObject(){
    5. }
    6. public static SingleObject getInstance(){
    7. return instance;
    8. }
    9. }

    线程安全,类加载就初始化,浪费内存,效率较高,(空间换时间)。

2. 懒汉式
  1. public class SingleObject{
  2. private static SingleObject instance = null;
  3. private SingleObject(){
  4. }
  5. public static SingleObject getInstance(){
  6. if ( instance == null ) {
  7. instance = new SingleObject();
  8. }
  9. return instance;
  10. }
  11. }

线程不安全,第一次调用初始化,避免内存浪费(时间换空间)

3. 懒汉式 + synchronized
  1. public class SingleObject{
  2. private static SingleObject instance = null;
  3. private SingleObject(){
  4. }
  5. // 直接在方法上加 synchronized,线程安全
  6. public static synchronized SingleObject getInstance(){
  7. if ( instance == null ) {
  8. instance = new SingleObject();
  9. }
  10. return instance;
  11. }
  12. }

线程安全,加了synchronized效率低,懒加载避免内存浪费。

4. 懒汉式 双重锁(Double Check Locking)+volatile
  1. public class SingleObject{
  2. // volatile防止指令重排
  3. private static volatile SingleObject instance = null;
  4. private SingleObject(){}
  5. public static SingleObject getInstance(){
  6. // 此层if判断,为提高效率
  7. if ( instance == null ) {
  8. synchronized ( SingleObject.class ) {
  9. if ( instance == null ) {
  10. instance = new SingleObject();
  11. }
  12. }
  13. }
  14. return instance;
  15. }
  16. }

线程安全,懒加载,效率较高。

5. 静态内部类
  1. public class SingleObject{
  2. private static class SingleHolder{
  3. private static final SingleObject INSTANCE = new SingleObject();
  4. }
  5. private SingleObject(){
  6. }
  7. public static final SingleObject getInstance(){
  8. return SingleHolder.INSTANCE;
  9. }
  10. }

getInstance()被调用时,SingleHolder才会被加载,在虚拟机初始化类的机制中,多线程同时加载类,会保证一个线程加载初始化,其他线程阻塞等待,所以是线程安全的。此模式的缺点是无法传参。

6.枚举类
  1. public enum SingleObject{
  2. INSTANCE;
  3. public void doSomething(){
  4. System.out.println("doSomething");
  5. }
  6. }

调用方法:

  1. public class Main{
  2. public static void main(String[] args){
  3. SingleObject.INSTANCE.doSomething();
  4. }
  5. }

单例模式的最佳实现方法,《Effective Java》作者推荐。不过不能通过反射调用私有构造方法。JDK1.5才有enum

二. 工厂模式

定义一个创建对象的接口,多子类实现,统一在工厂类定义选择子类实现的方法。

优点

  • 调用者想创建对象只需要知道其名称就可以了。
  • 扩展产品,只需要在工厂类扩展一个实现就行了。
  • 屏蔽产品的具体实现,只需要关心接口。

    缺点

  • 每增加一个产品,都需要增加一个具体类和对象实现工厂,增加了类的数量,增加系统类的依赖。

    使用场景

  1. 日志记录器:用户可以选择记录到本地,系统事件,服务器等。
  2. 数据库访问:提供系统多数据库访问支持。
  3. 多协议的连接服务器的框架。

    实现方法

    前提:
    1. public interface Cat{
    2. public void say();
    3. }
    1. public class whiteCat implement Cat{
    2. @Override
    3. public void say(){
    4. System.out.println("White Cat say");
    5. }
    6. }
    1. public class BlackCat implement Cat{
    2. @Override
    3. public void say(){
    4. System.out.println("Black Cat say");
    5. }
    6. }
    1.普通工厂模式
    1. public class CatFactory{
    2. public Cat getCat(String type){
    3. if ( type.equals("White")) {
    4. return new WhiteCat();
    5. }else if ( type.equals("Black")) {
    6. return new BlackCat();
    7. }else{
    8. System.out.println("请输入正确的类型!");
    9. return null;
    10. }
    11. }
    12. }
    调用例子:
    1. public class Test{
    2. public static void main(String[] args){
    3. CatFactory catFactory = new CatFactory();
    4. Cat cat = catFactory.getCat("Black");
    5. cat.say();
    6. }
    7. }

    结果输出: Black Cat say

2.多个工厂方法模式
  1. public class CatFactory{
  2. public Cat getWhiteCat(){
  3. return new WhiteCat();
  4. }
  5. public Cat getBlackCat(){
  6. return new BlackCat();
  7. }
  8. }

调用例子:

  1. public class Test{
  2. public static void main(String[] args){
  3. CatFactory catFactory = new CatFactory();
  4. Cat cat = catFactory.getBlackCat();
  5. cat.say();
  6. }
  7. }

结果输出: Black Cat say

3.多个工厂静态方法模式
  1. public class CatFactory{
  2. public static Cat getWhiteCat(){
  3. return new WhiteCat();
  4. }
  5. public static Cat getBlackCat(){
  6. return new BlackCat();
  7. }
  8. }

调用例子:

  1. public class Test{
  2. public static void main(String[] args){
  3. Cat cat = CatFactory.getBlackCat();
  4. cat.say();
  5. }
  6. }

结果输出: Black Cat say

总结:第三种用的比较多。

三. 建造者模式

将一个复杂对象的构建和表示分离,使得相同的构建过程可以有不同的表示。 与工厂模式类似,建造者模式更加关注和零件装配的顺序。复杂的对象创建使用工厂模式,更复杂的使用建造者模式。

优点

  • 封装性好,有较好的稳定性,需要增加相关对象不影响其他。
  • 客户端创建不需要知道产品组成的具体细节
  • 更精细的控制创建
  • 容易扩展

    缺点

  • 产品差异大不适合建造者模式

  • 产品变化复杂,导致定义多个具体产品类,使系统庞大

    应用场景

  1. 构造方法参数过多,产品对象有多个内部属性。
  2. 产品对象内部属性相互依赖。

    实现方法

    1. 方法链式
    1. public class Human{
    2. private String head;
    3. private String body;
    4. private String foot;
    5. public Human(String head, String body,String foot){
    6. this.head = head;
    7. this.body = body;
    8. this.foot = foot;
    9. }
    10. public static class HumanBuilder{
    11. private String head;
    12. private String body;
    13. private String foot;
    14. public Human.HumanBuilder head(String head){
    15. this.head = head;
    16. return this;
    17. }
    18. public Human.HumanBuilder body(String body){
    19. this.body = body;
    20. return this;
    21. }
    22. public Human.HumanBuilder foot(String foot){
    23. this.foot = foot;
    24. return this;
    25. }
    26. public Human build(){
    27. renturn new Human(this.head, this.body, this.foot);
    28. }
    29. }
    30. }
    1. public static void main(String[] args){
    2. Human human = Human.builder()
    3. .head("头")
    4. .body("身体")
    5. .foot("脚")
    6. .build();
    7. System.out.println("build success: " + human);
    8. }

    2. 原型式

    先定义人类

    1. @Data
    2. public class Human{
    3. private String head;
    4. private String body;
    5. private String foot;
    6. }

    造人的统一标准,造人的接口

    1. public interface BuildHuman{
    2. public void buildHead();
    3. public void buildBody();
    4. public void buildFoot();
    5. public Human createHuman();
    6. }

    造一个高智商的人

    1. public class SmartManBuilder implements BuildHuman{
    2. private Human human;
    3. public SmartManBuilder(){
    4. human = new Human();
    5. }
    6. @Override
    7. public void buildHead(){
    8. human.setHead("聪明的大脑");
    9. }
    10. @Override
    11. public void buildBody(){
    12. human.setBody("纤细的身体");
    13. }
    14. @Override
    15. public void buildFoot(){
    16. human.setFoot("灵活的腿");
    17. }
    18. @Override
    19. public Human createHuman(){
    20. return human;
    21. }
    22. }

    执行造人方法统一调用

    1. public class Director{
    2. public Human createHuman(BuildHuman bh){
    3. bh.buildHead();
    4. bh.buildBody();
    5. bh.buildFoot();
    6. return bh.createHuman();
    7. }
    8. }

    执行造人

    1. public class Test{
    2. public static void main(String[] args){
    3. Director director = new Director();
    4. Human human = director.createHuman();
    5. System.out.println(human.toString());
    6. }
    7. }

    四. 观察者模式

    一个对象被修改,则自动通知依赖它的对象。属于行为型模式。

    优点

  • 观察者和被观察者是抽象耦合的。
  • 建立一套触发机制。

    缺点

  • 被观察者有多个观察者,则花费很多时间通知。

  • 观察者和被观察者之间有循环依赖的话,可能导致系统崩溃。
  • 只能知道变化了,不知怎么变化的。

    应用场景

  1. 一个抽象模型有两个方面,其中一个方面依赖另一个方面。
  2. 一个对象改变导致其他对象改变,具体有多少对象不知。
  3. 一个对象必须通知其他对象,不知对象是谁。
  4. 系统中需要一个触发链。

    实现方法

    步骤1:创建Subject类

    1. public class Subject{
    2. private List<Observer> observers = new ArrayList<Observer>();
    3. private int state;
    4. public int getState(){
    5. return state;
    6. }
    7. public void setState(int state){
    8. this.state = state;
    9. notifyAllObservers();
    10. }
    11. public void attach(Observer observer){
    12. observers.add(observer);
    13. }
    14. public void notifyAllObservers(){
    15. for(Observer oberver : observers){
    16. observer.update();
    17. }
    18. }
    19. }

    步骤2:创建Observer类

    1. public abstract class Observer{
    2. protected Subject subject;
    3. public abstract void update();
    4. }

    步骤3:创建实体观察类

    1. public class BinaryObserver extends Observer{
    2. public BinaryObserver(Subject subject){
    3. this.subject = subject;
    4. this.subject.attach(this);
    5. }
    6. @Override
    7. public void update(){
    8. System.out.println("Binary String:" + Interger.toBinaryString(subject.getState()));
    9. }
    10. }
    1. public class HexaObserver extends Observer{
    2. public HexaObserver(Subject subject){
    3. this.subject = subject;
    4. this.subject.attach(this);
    5. }
    6. @Override
    7. public void update() {
    8. System.out.println( "Hex String: " +
    9. Integer.toHexString( subject.getState() ).toUpperCase() );
    10. }
    11. }

    调用

    1. public class Test{
    2. public static void main(String[] args){
    3. Subject subject = new Subject();
    4. new BinaryObserver(subject);
    5. new HexaObserver(subject);
    6. System.out.println("First state change: 15");
    7. subject.setState(15);
    8. System.out.println("Second state change: 10");
    9. subject.setState(10);
    10. }
    11. }

    结果

    First state change: 15 Binary String: 1111 Hex String: F Second state change: 10 Binary String: 1010 Hex String: A

五. 适配器模式

作为两个不兼容接口之间的桥梁,使原本接口不兼容的两接口一起工作。

优点

  • 可以让任何两个没有关联的类一起运行。
  • 提高了类的复用。
  • 增加了类的透明度。
  • 灵活性好。

    缺点

  • 过多使用导致系统凌乱,调用a接口内部却适配成b接口实现。过多适配器,应该进行重构。

  • 只能适配一个适配者类,目标类必须是抽象类。

    应用场景

  1. 美国110V,中国220V,适配器110转220V
  2. JDK1.1提供Enumeration接口,JDK1.2提供Iterrator接口,想用1.2得用适配器转化。
  3. Linux上运行Windows程序。

    实现方法

    定义媒体播放器和高级媒体播放器接口。

    1. public interface MediaPlayer{
    2. public void play(String audioType, String fileName);
    3. }
    1. public interface AdvancedMediaPlayer{
    2. public void playVlc(String fileName);
    3. public void playMp4(String fileName)
    4. }

    实现这两个类

    1. public class VlcPlayer implements AdvancedMediaPlayer{
    2. @Override
    3. public void playVlc(String fileName) {
    4. System.out.println("Playing vlc file. Name: "+ fileName);
    5. }
    6. @Override
    7. public void playMp4(String fileName) {
    8. //什么也不做
    9. }
    10. }
    1. public class Mp4Player implements AdvancedMediaPlayer{
    2. @Override
    3. public void playVlc(String fileName) {
    4. //什么也不做
    5. }
    6. @Override
    7. public void playMp4(String fileName) {
    8. System.out.println("Playing mp4 file. Name: "+ fileName);
    9. }
    10. }

    创建MediaPlayer接口的适配器类

    1. public class MediaAdapter implements MediaPlayer{
    2. private AdvancedMediaPlayer advancedMediaPlayer
    3. public MediaAdapter(String audioType){
    4. if(audioType.equalsIgnoreCase("vlc")){
    5. advancedMediaPlayer = new VlcPlayer();
    6. }else if(audioType.equalsIgnoreCase("mp4")){
    7. advancedMediaPlayer = new Mp4Player();
    8. }
    9. }
    10. @Override
    11. public void play(String audioType, String fileName){
    12. if(audioType.equalsIgnoreCase("vlc")){
    13. advancedMusicPlayer.playVlc(fileName);
    14. }else if(audioType.equalsIgnoreCase("mp4")){
    15. advancedMusicPlayer.playMp4(fileName);
    16. }
    17. }
    18. }

    创建实现MediaPlayer接口的实体类

    1. public class AudioPlayer implements MediaPlayer{
    2. MediaAdapter mediaAdapter;
    3. @Override
    4. public void play(String audioType, String fileName) {
    5. //播放 mp3 音乐文件的内置支持
    6. if(audioType.equalsIgnoreCase("mp3")){
    7. System.out.println("Playing mp3 file. Name: "+ fileName);
    8. }
    9. //mediaAdapter 提供了播放其他文件格式的支持
    10. else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
    11. mediaAdapter = new MediaAdapter(audioType);
    12. mediaAdapter.play(audioType, fileName);
    13. } else{
    14. System.out.println("Invalid media. "+ audioType + " format not supported");
    15. }
    16. }
    17. }

    调用

    1. public class Test{
    2. public static void main(String[] args){
    3. AudioPlayer audioPlayer = new AudioPlayer();
    4. audioPlayer.play("mp3", "beyond the horizon.mp3");
    5. audioPlayer.play("mp4", "alone.mp4");
    6. audioPlayer.play("vlc", "far far away.vlc");
    7. audioPlayer.play("avi", "mind me.avi");
    8. }
    9. }

    结果

    Playing mp3 file. Name: beyond the horizon.mp3 Playing mp4 file. Name: alone.mp4 Playing vlc file. Name: far far away.vlc Invalid media. avi format not supported

六. 迭代器模式

提供一种方法顺序访问一个聚合对象中的各个元素,无须暴露该对象的内部表示。用来遍历一个聚合对象。属于行为型模式。

优点

  • 支持不同方式遍历一个聚合对象
  • 迭代器简化了聚合类
  • 同一个聚合上可以有多个遍历
  • 低耦合,增加新的聚合类和迭代器都很方便,无须修改原有代码

    缺点

  • 数据存储和遍历数据的职责分离,使得类的数量增加,增加了系统的复杂度。

    应用场景

  1. 访问一个聚合对象的内容而无须暴露内部表示。
  2. 一个聚合对象需要多种遍历方式
  3. 为遍历不同聚合对象提供统一接口

    实现方法

    步骤1:先定义一个迭代器接口和容器接口

    1. public interface Iterator{
    2. public boolean hasNext();
    3. public Object next();
    4. }
    1. public interface Container{
    2. public Iterator getIterator();
    3. }

    步骤2:创建Container接口的实体类。

    1. public class NameRepository implements Container{
    2. public String names[] = {"zhangsan", "lisi", "wangwu", "xiaoliu"}
    3. @Override
    4. public Iterator getIterator(){
    5. return new NameInterator();
    6. }
    7. private class NameIterator implements Iterator{
    8. int index;
    9. @Override
    10. public boolean hasNext(){
    11. if(index < names.length){
    12. return true;
    13. }
    14. return false;
    15. }
    16. @Override
    17. public Object next(){
    18. if(this.hasNext()){
    19. return names[index++];
    20. }
    21. return null;
    22. }
    23. }
    24. }

    调用

    1. public class Test{
    2. public static void main(String[] args){
    3. NameRepository nameRepository = new NameRepository();
    4. for(Iterator iter = nameRepository.getIterator(); iter.hasNext();){
    5. String name = (String)iter.next();
    6. System.out.println("Name:" + name);
    7. }
    8. }
    9. }

    结果

    Name : zhangsan Name : lisi Name : wangwu Name : xiaoliu

七. 原型模式

用于创建重复的对象,同时能保证性能。该接口用于创建当前对象的克隆,直接创建代价比较大的时候用这种模式。

优点

  • 对资源进行优化,提高性能
  • 逃避构造函数的约束

    缺点

  • 配备克隆方法需要对全类的通盘考虑

  • 必须实现Cloneable接口

    应用场景

  1. 细胞分裂
  2. Java中的Object clone()方法

    实现方法

    步骤1:创建一个抽象类实现Cloneable接口

    1. public abstract class Shape implements Cloneable{
    2. private String id;
    3. protected String type;
    4. abstract void draw();
    5. public String getId(){
    6. return id;
    7. }
    8. public String getType(){
    9. return type;
    10. }
    11. public void setId(String id){
    12. this.id = id;
    13. }
    14. public Object clone(){
    15. Object clone = null;
    16. try {
    17. clone = super.clone();
    18. }catch (CloneNotSupportedExecption e){
    19. e.printStackTrace();
    20. }
    21. return clone;
    22. }
    23. }

    步骤2:创建扩展上面抽象类的实体类

    1. public class Rectangle extends Shape{
    2. public Rectangle(){
    3. type = "Rectangle";
    4. }
    5. @Override
    6. public void draw(){
    7. System.out.println("Inside Rectangle::draw() method.");
    8. }
    9. }
    1. public class Square extends Shape {
    2. public Square(){
    3. type = "Square";
    4. }
    5. @Override
    6. public void draw() {
    7. System.out.println("Inside Square::draw() method.");
    8. }
    9. }
    1. public class Circle extends Shape {
    2. public Circle(){
    3. type = "Circle";
    4. }
    5. @Override
    6. public void draw() {
    7. System.out.println("Inside Circle::draw() method.");
    8. }
    9. }

    步骤3:创建一个类,从数据库获取实体类,并存储在Hashtable中 ``` public class ShapeCache{ private static Hashtable shapeMap = new Hashtable();

    public static Shape getShape(String shapeId){

    1. Shape cachedShape = shapeMap.get(shapeId);
    2. return (Shape)cachedShape.clone();

    }

// 对每种形状都运行数据库查询,并创建该形状 // shapeMap.put(shapeKey, shape); // 例如,我们要添加三种形状 public static void loadCache() { Circle circle = new Circle(); circle.setId(“1”); shapeMap.put(circle.getId(),circle); Square square = new Square(); square.setId(“2”); shapeMap.put(square.getId(),square); Rectangle rectangle = new Rectangle(); rectangle.setId(“3”); shapeMap.put(rectangle.getId(),rectangle); } }

  1. 调用

public class Test{ public static void main(String[] args){ ShapeCache.loadCache();

  1. Shape clonedShape = (Shape) ShapeCache.getShape("1");
  2. System.out.println("Shape : " + clonedShape.getType());
  3. Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
  4. System.out.println("Shape : " + clonedShape2.getType());
  5. Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
  6. System.out.println("Shape : " + clonedShape3.getType());
  7. }

} ``` 结果

Shape : Circle Shape : Square Shape : Rectangle