常见设计模式总结
参考文章
- 设计模式 | 菜鸟教程
-
一. 单例模式
优点
单实例,低内存开销
-
缺点
没有接口,不能继承
- 与设计模式原则的单一职责冲突
应用场景
- 网站计数器
- 日志应用
- 配置对象类
- 数据库连接池
-
实现方法
1. 饿汉式
public class SingleObject{private static SingleObject instance = new SingleObject();// 私有化构造方法,使外部无法直接new获得对象private SingleObject(){}public static SingleObject getInstance(){return instance;}}
线程安全,类加载就初始化,浪费内存,效率较高,(空间换时间)。
2. 懒汉式
public class SingleObject{private static SingleObject instance = null;private SingleObject(){}public static SingleObject getInstance(){if ( instance == null ) {instance = new SingleObject();}return instance;}}
线程不安全,第一次调用初始化,避免内存浪费(时间换空间)
3. 懒汉式 + synchronized
public class SingleObject{private static SingleObject instance = null;private SingleObject(){}// 直接在方法上加 synchronized,线程安全public static synchronized SingleObject getInstance(){if ( instance == null ) {instance = new SingleObject();}return instance;}}
线程安全,加了synchronized效率低,懒加载避免内存浪费。
4. 懒汉式 双重锁(Double Check Locking)+volatile
public class SingleObject{// volatile防止指令重排private static volatile SingleObject instance = null;private SingleObject(){}public static SingleObject getInstance(){// 此层if判断,为提高效率if ( instance == null ) {synchronized ( SingleObject.class ) {if ( instance == null ) {instance = new SingleObject();}}}return instance;}}
线程安全,懒加载,效率较高。
5. 静态内部类
public class SingleObject{private static class SingleHolder{private static final SingleObject INSTANCE = new SingleObject();}private SingleObject(){}public static final SingleObject getInstance(){return SingleHolder.INSTANCE;}}
getInstance()被调用时,SingleHolder才会被加载,在虚拟机初始化类的机制中,多线程同时加载类,会保证一个线程加载初始化,其他线程阻塞等待,所以是线程安全的。此模式的缺点是无法传参。
6.枚举类
public enum SingleObject{INSTANCE;public void doSomething(){System.out.println("doSomething");}}
调用方法:
public class Main{public static void main(String[] args){SingleObject.INSTANCE.doSomething();}}
单例模式的最佳实现方法,《Effective Java》作者推荐。不过不能通过反射调用私有构造方法。JDK1.5才有enum
二. 工厂模式
定义一个创建对象的接口,多子类实现,统一在工厂类定义选择子类实现的方法。
优点
- 日志记录器:用户可以选择记录到本地,系统事件,服务器等。
- 数据库访问:提供系统多数据库访问支持。
- 多协议的连接服务器的框架。
实现方法
前提:public interface Cat{public void say();}
public class whiteCat implement Cat{@Overridepublic void say(){System.out.println("White Cat say");}}
public class BlackCat implement Cat{@Overridepublic void say(){System.out.println("Black Cat say");}}
1.普通工厂模式
调用例子:public class CatFactory{public Cat getCat(String type){if ( type.equals("White")) {return new WhiteCat();}else if ( type.equals("Black")) {return new BlackCat();}else{System.out.println("请输入正确的类型!");return null;}}}
public class Test{public static void main(String[] args){CatFactory catFactory = new CatFactory();Cat cat = catFactory.getCat("Black");cat.say();}}
结果输出: Black Cat say
2.多个工厂方法模式
public class CatFactory{public Cat getWhiteCat(){return new WhiteCat();}public Cat getBlackCat(){return new BlackCat();}}
调用例子:
public class Test{public static void main(String[] args){CatFactory catFactory = new CatFactory();Cat cat = catFactory.getBlackCat();cat.say();}}
结果输出: Black Cat say
3.多个工厂静态方法模式
public class CatFactory{public static Cat getWhiteCat(){return new WhiteCat();}public static Cat getBlackCat(){return new BlackCat();}}
调用例子:
public class Test{public static void main(String[] args){Cat cat = CatFactory.getBlackCat();cat.say();}}
结果输出: Black Cat say
三. 建造者模式
将一个复杂对象的构建和表示分离,使得相同的构建过程可以有不同的表示。 与工厂模式类似,建造者模式更加关注和零件装配的顺序。复杂的对象创建使用工厂模式,更复杂的使用建造者模式。
优点
- 封装性好,有较好的稳定性,需要增加相关对象不影响其他。
- 客户端创建不需要知道产品组成的具体细节
- 更精细的控制创建
-
缺点
产品差异大不适合建造者模式
- 产品变化复杂,导致定义多个具体产品类,使系统庞大
应用场景
- 构造方法参数过多,产品对象有多个内部属性。
-
实现方法
1. 方法链式
public class Human{private String head;private String body;private String foot;public Human(String head, String body,String foot){this.head = head;this.body = body;this.foot = foot;}public static class HumanBuilder{private String head;private String body;private String foot;public Human.HumanBuilder head(String head){this.head = head;return this;}public Human.HumanBuilder body(String body){this.body = body;return this;}public Human.HumanBuilder foot(String foot){this.foot = foot;return this;}public Human build(){renturn new Human(this.head, this.body, this.foot);}}}
public static void main(String[] args){Human human = Human.builder().head("头").body("身体").foot("脚").build();System.out.println("build success: " + human);}
2. 原型式
先定义人类
@Datapublic class Human{private String head;private String body;private String foot;}
造人的统一标准,造人的接口
public interface BuildHuman{public void buildHead();public void buildBody();public void buildFoot();public Human createHuman();}
造一个高智商的人
public class SmartManBuilder implements BuildHuman{private Human human;public SmartManBuilder(){human = new Human();}@Overridepublic void buildHead(){human.setHead("聪明的大脑");}@Overridepublic void buildBody(){human.setBody("纤细的身体");}@Overridepublic void buildFoot(){human.setFoot("灵活的腿");}@Overridepublic Human createHuman(){return human;}}
执行造人方法统一调用
public class Director{public Human createHuman(BuildHuman bh){bh.buildHead();bh.buildBody();bh.buildFoot();return bh.createHuman();}}
执行造人
public class Test{public static void main(String[] args){Director director = new Director();Human human = director.createHuman();System.out.println(human.toString());}}
四. 观察者模式
优点
- 一个抽象模型有两个方面,其中一个方面依赖另一个方面。
- 一个对象改变导致其他对象改变,具体有多少对象不知。
- 一个对象必须通知其他对象,不知对象是谁。
-
实现方法
步骤1:创建Subject类
public class Subject{private List<Observer> observers = new ArrayList<Observer>();private int state;public int getState(){return state;}public void setState(int state){this.state = state;notifyAllObservers();}public void attach(Observer observer){observers.add(observer);}public void notifyAllObservers(){for(Observer oberver : observers){observer.update();}}}
步骤2:创建Observer类
public abstract class Observer{protected Subject subject;public abstract void update();}
步骤3:创建实体观察类
public class BinaryObserver extends Observer{public BinaryObserver(Subject subject){this.subject = subject;this.subject.attach(this);}@Overridepublic void update(){System.out.println("Binary String:" + Interger.toBinaryString(subject.getState()));}}
public class HexaObserver extends Observer{public HexaObserver(Subject subject){this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println( "Hex String: " +Integer.toHexString( subject.getState() ).toUpperCase() );}}
调用
public class Test{public static void main(String[] args){Subject subject = new Subject();new BinaryObserver(subject);new HexaObserver(subject);System.out.println("First state change: 15");subject.setState(15);System.out.println("Second state change: 10");subject.setState(10);}}
结果
First state change: 15 Binary String: 1111 Hex String: F Second state change: 10 Binary String: 1010 Hex String: A
五. 适配器模式
作为两个不兼容接口之间的桥梁,使原本接口不兼容的两接口一起工作。
优点
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
-
缺点
过多使用导致系统凌乱,调用a接口内部却适配成b接口实现。过多适配器,应该进行重构。
- 只能适配一个适配者类,目标类必须是抽象类。
应用场景
- 美国110V,中国220V,适配器110转220V
- JDK1.1提供Enumeration接口,JDK1.2提供Iterrator接口,想用1.2得用适配器转化。
-
实现方法
定义媒体播放器和高级媒体播放器接口。
public interface MediaPlayer{public void play(String audioType, String fileName);}
public interface AdvancedMediaPlayer{public void playVlc(String fileName);public void playMp4(String fileName)}
实现这两个类
public class VlcPlayer implements AdvancedMediaPlayer{@Overridepublic void playVlc(String fileName) {System.out.println("Playing vlc file. Name: "+ fileName);}@Overridepublic void playMp4(String fileName) {//什么也不做}}
public class Mp4Player implements AdvancedMediaPlayer{@Overridepublic void playVlc(String fileName) {//什么也不做}@Overridepublic void playMp4(String fileName) {System.out.println("Playing mp4 file. Name: "+ fileName);}}
创建MediaPlayer接口的适配器类
public class MediaAdapter implements MediaPlayer{private AdvancedMediaPlayer advancedMediaPlayer;public MediaAdapter(String audioType){if(audioType.equalsIgnoreCase("vlc")){advancedMediaPlayer = new VlcPlayer();}else if(audioType.equalsIgnoreCase("mp4")){advancedMediaPlayer = new Mp4Player();}}@Overridepublic void play(String audioType, String fileName){if(audioType.equalsIgnoreCase("vlc")){advancedMusicPlayer.playVlc(fileName);}else if(audioType.equalsIgnoreCase("mp4")){advancedMusicPlayer.playMp4(fileName);}}}
创建实现MediaPlayer接口的实体类
public class AudioPlayer implements MediaPlayer{MediaAdapter mediaAdapter;@Overridepublic void play(String audioType, String fileName) {//播放 mp3 音乐文件的内置支持if(audioType.equalsIgnoreCase("mp3")){System.out.println("Playing mp3 file. Name: "+ fileName);}//mediaAdapter 提供了播放其他文件格式的支持else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){mediaAdapter = new MediaAdapter(audioType);mediaAdapter.play(audioType, fileName);} else{System.out.println("Invalid media. "+ audioType + " format not supported");}}}
调用
public class Test{public static void main(String[] args){AudioPlayer audioPlayer = new AudioPlayer();audioPlayer.play("mp3", "beyond the horizon.mp3");audioPlayer.play("mp4", "alone.mp4");audioPlayer.play("vlc", "far far away.vlc");audioPlayer.play("avi", "mind me.avi");}}
结果
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:先定义一个迭代器接口和容器接口
public interface Iterator{public boolean hasNext();public Object next();}
public interface Container{public Iterator getIterator();}
步骤2:创建Container接口的实体类。
public class NameRepository implements Container{public String names[] = {"zhangsan", "lisi", "wangwu", "xiaoliu"}@Overridepublic Iterator getIterator(){return new NameInterator();}private class NameIterator implements Iterator{int index;@Overridepublic boolean hasNext(){if(index < names.length){return true;}return false;}@Overridepublic Object next(){if(this.hasNext()){return names[index++];}return null;}}}
调用
public class Test{public static void main(String[] args){NameRepository nameRepository = new NameRepository();for(Iterator iter = nameRepository.getIterator(); iter.hasNext();){String name = (String)iter.next();System.out.println("Name:" + name);}}}
结果
Name : zhangsan Name : lisi Name : wangwu Name : xiaoliu
七. 原型模式
用于创建重复的对象,同时能保证性能。该接口用于创建当前对象的克隆,直接创建代价比较大的时候用这种模式。
优点
- 细胞分裂
-
实现方法
步骤1:创建一个抽象类实现Cloneable接口
public abstract class Shape implements Cloneable{private String id;protected String type;abstract void draw();public String getId(){return id;}public String getType(){return type;}public void setId(String id){this.id = id;}public Object clone(){Object clone = null;try {clone = super.clone();}catch (CloneNotSupportedExecption e){e.printStackTrace();}return clone;}}
步骤2:创建扩展上面抽象类的实体类
public class Rectangle extends Shape{public Rectangle(){type = "Rectangle";}@Overridepublic void draw(){System.out.println("Inside Rectangle::draw() method.");}}
public class Square extends Shape {public Square(){type = "Square";}@Overridepublic void draw() {System.out.println("Inside Square::draw() method.");}}
public class Circle extends Shape {public Circle(){type = "Circle";}@Overridepublic void draw() {System.out.println("Inside Circle::draw() method.");}}
步骤3:创建一个类,从数据库获取实体类,并存储在Hashtable中 ``` public class ShapeCache{ private static Hashtable
shapeMap = new Hashtable (); public static Shape getShape(String shapeId){
Shape cachedShape = shapeMap.get(shapeId);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); } }
调用
public class Test{ public static void main(String[] args){ ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");System.out.println("Shape : " + clonedShape.getType());Shape clonedShape2 = (Shape) ShapeCache.getShape("2");System.out.println("Shape : " + clonedShape2.getType());Shape clonedShape3 = (Shape) ShapeCache.getShape("3");System.out.println("Shape : " + clonedShape3.getType());}
} ``` 结果
Shape : Circle Shape : Square Shape : Rectangle
