在了解java常见的设计模式之前,我们要了解三个问题,
1.为什么要了解设计模式?
2.这些设计模式的是根据什么原则来设计的?
3.有哪些常见的设计模式?
- 为什么要了解设计模式?
因为设计模式代表了最佳的实践,是很多优秀的软件开发人员的经验总结,恰当的使用设计模式可以增强代码的可复用性,可维护性,可扩展性,健壮性及安全性。
- 这些设计模式的是根据什么原则来设计的?
这些设计模式的基础其实就是java面向对象,而且大部分的设计原则就是根据oop的设计原则(开闭原则、里氏替换原则、迪米特原则(最少知识原则)、单一职责原则(DIP)、接口分离原则(ISP)、依赖倒置原则、组合/聚合复用原则),目的很简单,就是体现java三大特点,为了更好的体现出程序、代码的灵活性。
- 有哪些常见的设计模式?
经典的设计模式有23种,此篇文章介绍4种常见的设计模式,单例模式、工厂模式、责任链模式和观察者模式。
1.单例模式
1.1 概念
顾名思义,保证在内存中只用一个实例
1.2 使用场景
比如:系统配置文件的管理,这些配置文件只要使用一个单例对象进行读写即可,系统总其他地方需要使用配置信息时,只要使用该单例对象进行获取就可以了,这样便于统一管理配置信息。
1.3 优缺点
优点:
- 在内存中只有一个对象,节省内存空间;
- 避免频繁的创建销毁对象,可以提高性能;
- 避免对共享资源的多重占用,简化访问;
- 为整个系统提供一个全局访问点。
缺点:
- 不适用于变化频繁的对象;
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
1.4 示例
饥饿模式
/**
* 单例模式,饥饿加载
*/
public class SingletonDemo {
//1. 需要有一个私有的构造函数,防止该类通过new的方式创建实例
private SingletonDemo(){}
//2. 饥饿模式,首先生成一个实例
private static final SingletonDemo instance = new SingletonDemo();
//3. 静态方法,用于获取已经生成的实例
public static SingletonDemo getInstance() {
return instance;
}
public String hello(String name) {
return "hello " + name;
}
}
这种直线方式简单,在加载类的时候就会创建好实例,等待被调用,且是线程安全的。
懒汉模式
第一种写法:
/**
* 单例模式: 懒汉式
*/
public class SingletonDemo02 {
private SingletonDemo02(){}
private static SingletonDemo02 singletonDemo02 = null;
public static SingletonDemo02 getInstance() {
if (singletonDemo02 == null) {
singletonDemo02 = new SingletonDemo02();
}
return singletonDemo02;
}
public String hello(String name) {
return "hello " + name;
}
}
注意: 这种方式在多线程访问时会有问题,具体原因就是如果线程多的时候,由于时间片的轮换机制,可能会导致产生多个实例。
第二种写法:
/**
* 单例模式: 懒汉式,线程安全,但性能较低
*/
public class SingletonDemo03 {
private SingletonDemo03() {
}
private static SingletonDemo03 singletonDemo03 = null;
public static synchronized SingletonDemo03 getInstance(){
if(singletonDemo03 == null) {
singletonDemo03 = new SingletonDemo03();
}
return singletonDemo03;
}
public String hello(String name) {
return "hello " + name;
}
}
第三种写法:
public class SingletonDemo03 {
private SingletonDemo03() {
}
private static SingletonDemo03 singletonDemo03 = null;
public static SingletonDemo03 getInstance(){
//系统减小同步块来提升性能
if(singletonDemo03 == null) {
synchronized (SingletonDemo03.class) {
singletonDemo03 = new SingletonDemo03();
}
}
return singletonDemo03;
}
public String hello(String name) {
return "hello " + name;
}
}
该方式依然会有线程安全问题,原因跟第一种原因一种
第四种写法:
/**
* 单例模式: 懒汉式,双重检查单例
*/
public class SingletonDemo03 {
private SingletonDemo03(){
}
private static SingletonDemo03 singletonDemo03 = null;
public static SingletonDemo03 getInstance(){
//减小同步块,并使用双重检查来保证线程安装
if(singletonDemo03 == null) {
synchronized (SingletonDemo03.class) {
if(singletonDemo03 == null) {
singletonDemo03 = new SingletonDemo03();
}
}
}
return singletonDemo03;
}
public String hello(String name) {
return "hello " + name;
}
}
第五种写法:
/**
* 单例模式: 懒加载, 线程安全
*/
public class SingletonDemo04 {
//阻止外部实例化
private SingletonDemo04(){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//使用静态内部类来使用一个SingletonDemo04对象
private static class SingletonDemoHolder {
private final static SingletonDemo04 instance = new SingletonDemo04();
}
public static SingletonDemo04 getInstance() {
return SingletonDemoHolder.instance;
}
public String hello(String name) {
return "hello " + name;
}
}
第六种写法:
public enum SingletonDemo05 {
INSTANCE;
public String hello(String name) {
return "hello " + name;
}
}
可以保证单例,且线程安全,同时也是最简单的,因为枚举类型不能够被实例化,前5种方式还是可以通过java的反射机制获取新的实例,但是枚举类型的不可以,因为一运行,就由java虚拟机jvm实例,别人是示例不了的。
2 工厂模式
2.1 概念
用于产生对象的方法或者式类,称之为工厂。 上面所讲到的单例模式也可以看作为一个特殊的工厂。
2.2 使用场景
为什么需要工作模式,原来使用new的方式感觉也很简单啊,而且也很容易看明白啊。
使用工厂的原因是我们可以通过工厂模式,来集中控制对象的创建过程,这样可以给设计带来更多的灵活性。
比如:spring的IOC容器就是工厂模式的经典实现。
2.3 工厂方法(简单工厂)
用于生产指定系列的对象。已鸭子为例,鸭子有真的鸭子,橡皮鸭,电子玩具鸭等。如何能方便的创建出各种鸭子,并将创建过程控制起来,以便于以后的维护和扩展
类图:
代码:
抽象类,鸭子的规范类
public abstract class Duck {
abstract public void quack();
}
三个子类
public class RubberDuck extends Duck {
@Override
public void quack() {
System.out.println("我是橡皮鸭,");
}
}
public class WildDuck extends Duck {
@Override
public void quack() {
System.out.println("我是真鸭子");
}
}
public class DonaldDuck extends Duck {
@Override
public void quack() {
System.out.println("我是唐老鸭");
}
}
工厂类
public class DuckFactory {
private DuckFactory(){}
private static DuckFactory duckFactory = new DuckFactory();
public static final int WILD_DUCK = 1;
public static final int RUBBER_DUCK = 2;
public static final int DONALD_DUCK = 3;
public static final int PJ_DUCK = 4;
public static final int ZH_DUCK = 5;
public static Duck getInstance(int duckType) {
switch (duckType) {
case WILD_DUCK:
return new WildDuck();
case RUBBER_DUCK:
return new RubberDuck();
case DONALD_DUCK:
return new DonaldDuck();
case PJ_DUCK:
return new PJDuck();
case ZH_DUCK:
return new ZhouHeiDuck();
default:
return null;
}
}
测试类
public class Main {
public static void main(String[] args) {
Duck donaldDuck = DuckFactory.getInstance(DuckFactory.DONALD_DUCK);
donaldDuck.quack();
Duck wildDuck = DuckFactory.getInstance(DuckFactory.WILD_DUCK);
wildDuck.quack();
}
}
输出结果:
我是唐老鸭
我是真鸭子
好处:只要给定特定的鸭子类型,就可以实例化出对应的实体,这样会利于修改和拓展,体现出更好的灵活性。
2.4 抽象工厂
用于生成指定产品族,一个产品族中包括多种产品。例如:
我们都比较熟悉的电脑制造相关行业,有HP,罗技,联想,戴尔,近几年华为,小米也进来了,每个生产商生产的电脑又包括鼠标,键盘,屏幕等等配件。此时我们需要使用工厂模式来进行管理不同的产品族,这时使用简单工厂(也有叫作工厂方法的)已经无法满足要求,此时可以使用抽象工厂。
类图:
示例代码:
PcFactory(类族)
public abstract class PcFactory {
//制作方法
public abstract Mouse makeMouse();
public abstract Keyboard makeKeyboard();
//为得到具体的工厂的方法服务
private static HpFactory hpFactory = new HpFactory();
private static LogicFactory logicFactory = new LogicFactory();
//为得到具体的工厂的方法服务
public final static int PC_TYPE_HP = 1;
public final static int PC_TYPE_LG = 2;
/**
* 得到具体的工厂的方法
* @param pcType传入表示电脑类型的常数
* @return 返回PcFactory抽象类:面向抽象编程代替面向具体编程
*/
public static PcFactory getPcFactory(int pcType) {
switch (pcType){
case 1:
return hpFactory;
case 2 :
return logicFactory;
default:
return null;
}
}
}
HPFactory(惠普)工厂
public class HpFactory extends PcFactory {
//返回抽象类:面向抽象编程代替面向具体编程
@Override
public Mouse makeMouse() {
return new HpMouse();
}
@Override
public Keyboard makeKeyboard() {
return new HpKeyboard();
}
}
LogicFactory(罗技)子工厂(继承抽象类PcFactory)
public class LogicFactory extends PcFactory {
@Override
public Mouse makeMouse() {
return new LogicMouse();
}
@Override
public Keyboard makeKeyboard() {
return new LogicKeyboard();
}
}
鼠标抽象工厂Keyboard
public abstract class Keyboard {
abstract String getInfo();
}
Hpkeyboard(HP的键盘制作工厂)
public class HpKeyboard extends Keyboard {
@Override
String getInfo() {
return "HP keyboard";
}
}
LogicKeyboard(Logic的键盘制作工厂)
public class LogicKeyboard extends Keyboard {
@Override
String getInfo() {
return "logic keyboard";
}
}
键盘抽象工厂Mouse
public abstract class Mouse {
abstract String getInfo();
}
HpMouse(HP的鼠标制作工厂)
public class HpMouse extends Mouse {
@Override
String getInfo() {
return "HP mouse";
}
}
LogicMouse (Logic的鼠标制作工厂)
public class LogicMouse extends Mouse {
@Override
String getInfo() {
return "logic mouse";
}
}
测试
public class Main {
public static void main(String[] args) {
//通过抽象PcFactory父类得到HP电脑制作工厂
PcFactory HpFactory = PcFactory.getPcFactory(PcFactory.PC_TYPE_HP);
//得到HP制作键盘的方法
Keyboard keyboard = HpFactory.makeKeyboard();
//得到HP制作鼠标的方法
Mouse mouse = HpFactory.makeMouse();
System.out.println(keyboard.getInfo());
System.out.println(mouse.getInfo());
}
}
结果:
HP keyboard
HP mouse
3.责任链模式
3.1 概念
责任链模式是一个对象的行为模式,很多对象之间形成一条链条,处理请求在这个链条上进行传递,直到责任链的上的某个对象决定处理请求(也可扩展为几个对象处理),这个过程对于用户来说是透明的,也就是说用户并不需要知道是责任链上的哪个对象处理的请求,对请求是否处理由链条上的对象自己决定。
为了便于理解我们可以想象一下击鼓传花的游戏。
3.2 使用场景
web容器中的过滤器算是责任链模式的一个经典场景。另外举个例子:当在论坛上提交内容时,论坛系统需要对一些关键词进行处理,看看有没有包含一些敏感词汇,而这些敏感词汇我们可以使用责任链模式进行处理。
3.3 类图
代码:
Filter接口
/**
* Filter接口,实际上是对变化的抽象
* 这种方式会逐个的运行Filter,但不能
* 指定是否需要继续执行后面的Filter。
* 比如:当发现违法了特殊符号的Filter时
* 其后的过滤链没有必要执行
*/
public interface Filter {
void doFilter(Message message);
}
CheckSyntaxFiler(对语法结构进行检查)
对语法进行检查不能出现”<>”如果出现了用“#”替换
public class ChackSyntaxFilter implements Filter {
@Override
public void doFilter(Message message) {
String content = message.getContent();
content = content.replace("<", "#");
content = content.replace(">", "#");
message.setContent(content);
}
}
WordFilter(敏感词的过滤)
若出现敏感词汇用”***”替换
public class WordFilter implements Filter {
@Override
public void doFilter(Message message) {
String content = message.getContent();
content = content.replace("嘻嘻", "***");
content = content.replace("hha", "***");
message.setContent(content);
}
}
FilterChain(过滤器链)
/**
* 将Filter组织成一个链条
*/
public class FilterChain {
private FilterChain(){}
private static List<Filter> filters = new ArrayList<>();
private static FilterChain instance = new FilterChain();
public static FilterChain getInstance(){
return instance;
}
public FilterChain add(Filter filter) {
filters.add(filter);
return this;
}
public Message dofilters(final Message message) {
for (Filter f : filters) {
f.doFilter(message);
}
return message;
}
}
Message(demo)
package com.zking.patterndemo;
public class Message {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Message{" +
"content='" + content + '\'' +
'}';
}
}
测试
public class Main {
public static void main(String[] args) {
Message msg = new Message();
msg.setContent("hello, <abc>, hhaxx嘻嘻, 哈哈哈");
FilterChain fc = FilterChain.getInstance();
fc.add(new ChackSyntaxFilter())
.add(new WordFilter())
.dofilters(msg);
System.out.println(msg.getContent());
}
}
结果:
hello,#abc#,***xx***,哈哈哈
4.观察者模式
4.1 概念
观察者模式是对象的行为模式,有时也称为“发布/订阅模式”或者“监听器模式”。
观察者模式定义了观察者和被观察者之间的一对多的关系,让多个观察者对象可以响应一个被观察者对象。
4.2 使用场景
比较经典的使用场景,比如:java中的swing包中对事件的处理。浏览器对鼠标,键盘等事件的处理等, spring中的事件发布机制也是使用该模式。
4.3 类图
代码
Observer(观察者接口)
public interface Observer {
void bell(BellEvent event);
}
Nurse(护士)
public class Nurse implements Observer {
@Override
public void bell(BellEvent event) {
System.out.println("I am nurse, Can I help you?");
}
}
Docter(医生)
public class Docter implements Observer {
@Override
public void bell(BellEvent event) {
System.out.println("I am docter, Can I help you?");
}
}
Wife(妻子)
public class Wife implements Observer {
@Override
public void bell(BellEvent event) {
System.out.println("baby, I am here, Don't worry !");
}
}
Event(总事件)
public abstract class Event {
protected Object source;
public Object getSource() {
return this.source;
}
}
BellEvent(事件实现类)
public class BellEvent extends Event {
long timestamp;
public BellEvent(Object source) {
this.timestamp = System.currentTimeMillis();
this.source = source;
}
}
Patient(患者)
public class Patient {
private List<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void ringBell() {
BellEvent event = new BellEvent(this);
for (Observer observer: observers) {
observer.bell(event);
}
}
}
测试
public class Main {
public static void main(String[] args) {
Patient patient = new Patient();
patient.addObserver(new Docter());
patient.addObserver(new Nurse());
patient.addObserver(new Wife());
patient.ringBell();
}
}
结果:
I am docter, Can I help you?
I am nurse, Can I help you?
baby, I am here, Don't worry !
4.4 小结
观察者模式是使用的非常广泛,比如:Listener,Hook,Callback等等,其实都是观察者的一种应用,名称叫法不同而已,思路基本相同。
总结
这些都是比较继承的设计模式,很多框架都会运用这些设计模式,只有理解了这些设计模式,才能更好的去了解和使用框架