常见设计模式总结
参考文章
- 设计模式 | 菜鸟教程
-
一. 单例模式
优点
单实例,低内存开销
-
缺点
没有接口,不能继承
- 与设计模式原则的单一职责冲突
应用场景
- 网站计数器
- 日志应用
- 配置对象类
- 数据库连接池
-
实现方法
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{
@Override
public void say(){
System.out.println("White Cat say");
}
}
public class BlackCat implement Cat{
@Override
public 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. 原型式
先定义人类
@Data
public 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();
}
@Override
public void buildHead(){
human.setHead("聪明的大脑");
}
@Override
public void buildBody(){
human.setBody("纤细的身体");
}
@Override
public void buildFoot(){
human.setFoot("灵活的腿");
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
@Override
public void playMp4(String fileName) {
//什么也不做
}
}
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
//什么也不做
}
@Override
public 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();
}
}
@Override
public 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;
@Override
public 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"}
@Override
public Iterator getIterator(){
return new NameInterator();
}
private class NameIterator implements Iterator{
int index;
@Override
public boolean hasNext(){
if(index < names.length){
return true;
}
return false;
}
@Override
public 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";
}
@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 Circle extends Shape {
public Circle(){
type = "Circle";
}
@Override
public 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