前言
设计模式目的是为了代码可重用,降低代码的耦合度,提高代码的可扩展性、可维护性,不可为了使用设计模式而过度设计,要平衡复杂度和灵活性。
设计原则
设计模式一般需要符合以下几个原则:
开闭原则
开放扩展,关闭修改。简单来说,在添加新功能时能不修改就不修改,通过新增代码可以实现那是最好的。
里氏替换原则
一种面向对象的设计原则。简单来说,一个方法如果可以通过父类调用成功,那么替换成子类调用也可以成功。
单一职责
一个类只负责一个功能领域中相应的职责。
迪米特法则
最少知道原则,不希望类之间建立直接联系,降低耦合度,如果真的需要希望通过中介通信。
目前常见的设计模式分为以下3个类型:
创建型模式
封装了系统中对象如何创建、组合等信息。重点关注如何创建对象,将对象的创建和使用分离。
工厂方法
通过工厂生产的形式获取对象,客户端引用抽象工厂及抽象产品,不关注如何创建。
应用案例:Spring的BeanFactory
代码示例:
代码语言:java复制//工厂类
public interface IFactory {
IProduct create();
}
代码语言:java复制//具体工厂实现
public class FactoryA implements IFactory{
@Override
public IProduct create() {
return new ProductA();
}
}
代码语言:java复制// 产品抽象类
public interface IProduct {
void get();
}
代码语言:java复制//具体产品实现
public class ProductA implements IProduct{
@Override
public void get() {}
}
代码语言:java复制public class FactoryMain {
public static void main(String[] args) {
IFactory factory=new FactoryA();
IProduct product=factory.create();
product.get();
}
}
抽象工厂
基于工厂方法,在工厂中生产多个产品,也是与其的区别。
代码示例:
代码语言:java复制//抽象工厂类
public interface IFactory {
//抽象工厂中多个产品或产品组
IProductA createA();
IProductB createB();
}
代码语言:java复制//抽象工厂具体实现
public class FactoryA implements IFactory {
//抽象工厂具体产品指向
@Override
public IProductA createA() {
return new ProductA();
}
@Override
public IProductB createB() {
return new ProductB();
}
}
代码语言:java复制//产品或产品组A
public interface IProductA {
void get();
}
代码语言:java复制//产品或产品组B
public interface IProductB {
void get();
}
代码语言:java复制//产品或产品组A具体实现
public class ProductA implements IProductA{
@Override
public void get() {}
}
代码语言:java复制//产品或产品组B具体实现
public class ProductB implements IProductB{
@Override
public void get() {}
}
代码语言:java复制public class FactoryMain {
public static void main(String[] args) {
// FactoryA 工厂下的 IProductA和IProductB
IFactory factoryA=new FactoryA();
IProductA AproductA=factoryA.createA();
AproductA.get();
IProductB AproductB=factoryA.createB();
AproductB.get();
}
}
单例
保证一个类只有一个实例,并提供全局访问。
应用案例:Spring的SingletonBeanRegistry
代码示例:
代码语言:java复制public class SingletenInstance {
public static final SingletenInstance instance=new SingletenInstance();
private SingletenInstance(){};
public static void main(String[] args) {
SingletenInstance instance1=SingletenInstance.instance;
SingletenInstance instance2=SingletenInstance.instance;
System.out.println(instance1==instance2);//true
}
}
原型
创建对象时根据现有的对象创建。
应用案例:java的Object提供的clone()方法
代码示例:
代码语言:java复制public class PrototypeObj implements Cloneable{
@Override
protected PrototypeObj clone() throws CloneNotSupportedException {
return new PrototypeObj();
}
public static void main(String[] args) throws CloneNotSupportedException {
PrototypeObj obj1=new PrototypeObj();
PrototypeObj obj2=obj1.clone();
}
}
建造者(生成器)
将复杂对象的构建过程抽象出来,通过不同的实现构造不同表现的对象。
应用场景:StringBuilder
代码示例:
代码语言:java复制public class BuilderObj {
private String attr1;
private String attr2;
private String attr3;
public BuilderObj build() {
return this;
}
public BuilderObj setAttr1(String attr1) {
this.attr1 = attr1;
return this;
}
public BuilderObj setAttr2(String attr2) {
this.attr2 = attr2;
return this;
}
public BuilderObj setAttr3(String attr3) {
this.attr3 = attr3;
return this;
}
public static void main(String[] args) {
BuilderObj obj = new BuilderObj().setAttr1("Attr1").setAttr3("Attr3").build();
}
}
结构型模式
如何组合己有的类和对象以获得更大的结构。重点关注如何组合对象,通过组合对象灵活使用获得更好、更灵活的结构。
适配器
将一个接口转换成提供者需要的接口,通过对接口的转换使得两个本来不兼容的接口可以一起工作。
应用案例:ExecutorService对Callable及Runnable的兼容
代码示例:
代码语言:java复制public class RunnableAdapter implements Runnable{
private Callable callable;
//适配Callable接口
public RunnableAdapter(Callable callable) {
this.callable=callable;
}
@Override
public void run() {
try {
System.out.println(callable.call());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Callable callable =()->"I'm call";
new Thread(new RunnableAdapter(callable)).start();
}
}
装饰器
简单来说,动态给对象添加一些额外的功能。
应用案例:FilterInputStream就属于一个装饰类,其继承InputStream,所有InputStream可以通过FilterInputStream下面的类进行装饰获取额外功能,比如通过BufferedInputStream缓冲功能提高读取效率、通过DataInputStream读数据时可以直接转换java基本类型等。
代码示例:
代码语言:java复制public interface Keyboard {
public void print();
}
代码语言:java复制public class KeyboardNormal implements Keyboard{
public void print(){
System.out.println("按键");
}
}
代码语言:java复制public class KeyboardLight implements Keyboard{
private Keyboard keyboard;
//装饰对象
public KeyboardLight(Keyboard keyboard) {
this.keyboard=keyboard;
}
public void print(){
this.keyboard.print();
System.out.println("闪光效果");
}
}
代码语言:java复制public class KeyboardVoice implements Keyboard{
private Keyboard keyboard;
//装饰对象
public KeyboardVoice(Keyboard keyboard) {
this.keyboard=keyboard;
}
public void print(){
this.keyboard.print();
System.out.println("声音效果");
}
}
代码语言:java复制 public static void main(String[] args) {
Keyboard keyboardNormal=new KeyboardNormal();
//装饰对象灯光效果
Keyboard keyboardLight=new KeyboardLight(keyboardNormal);
//装饰对象声音效果
Keyboard keyboardVoice=new KeyboardVoice(keyboardLight);
keyboardVoice.print();
//按键
//闪光效果
//声音效果
}
享元
核心思想是共享,如果一个对象一经创建很少变动,那直接返回共享实例即可(重复利用对象)。
应用案例:可以看下Integer.valueOf(),在初始化数据时如果是-128~127就直接返回数据。线程池等。
代码示例:
代码语言:java复制public class FlyWeightObj {
String cache = "value";
public String get() {
if (null != cache) {
return cache;
}
return new String("chche");
}
}
代理
通过一个代理对象控制一个对象的访问,和适配器模式相似,不过是同一接口。
应用案例:Spring AOP
代码示例:
代码语言:java复制public class RunnableProxy implements Runnable{
private Runnable runnable;
//代理Runnable对象
public RunnableProxy(Runnable runnable) {
this.runnable=runnable;
}
@Override
public void run() {
//Runnable执行代理
System.out.println("do something");
runnable.run();
System.out.println("do something");
}
public static void main(String[] args) {
new Thread(new RunnableProxy(()-> System.out.println("hi"))).start();
}
}
外观
比较简单的一个模式,基本思想是:如果客户端需要和多个子系统交互,那么可以提供一个中介,客户端和中介交互,中介和各子系统交互。
应用案例:网关、nginx
组合
常用于树形数据结构,把叶子结点和父节点统一管理,比如菜单。
代码示例:
代码语言:java复制public class Menu {
private String name;
private List<Menu> childMenu;
}
行为型模式
用于对象之间的职责及其提供服务的分配方式。重点关注对象如何协作,描述一组对象如何协作完成一个整体任务。
责任链
一种处理请求的模式,把多个处理器串成链,依次处理,能让多个处理器都有机会处理,或者某个处理器请求成功为止。
应用场景:Filter,Interceptor
代码示例:
代码语言:java复制public interface Filter {
boolean doFilter();
}
代码语言:java复制public class Filter1 implements Filter{
@Override
public boolean doFilter() {
System.out.println("Filter1.....");
return false;
}
}
代码语言:java复制public class Filter2 implements Filter{
@Override
public boolean doFilter() {
System.out.println("Filter2.....");
return false;
}
}
代码语言:java复制//责任链
public class FilterChain {
private List<Filter> filters=new ArrayList<>();
//将多个处理器串联起来
public void add(Filter filter){
filters.add(filter);
}
public boolean doFilter(){
//串联执行
for (Filter filter:filters){
if (!filter.doFilter())return false;
}
return true;
}
public static void main(String[] args) {
FilterChain chain=new FilterChain();
chain.add(new Filter1());
chain.add(new Filter2());
chain.doFilter();
}
}
备忘录
捕捉一个对象当时的状态,并在该对象之外保存这个状态,在某个时候可以恢复状态。
应用场景:几乎所有的软件都用到了备忘录模式。比如说保存、回退。
代码示例:
代码语言:java复制public class Memento implements Serializable{
//备忘对象
private MementoObj obj = new MementoObj();
private class MementoObj implements Serializable {
private String state = "old";
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
//记录状态
public void save() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("obj.data")));
oos.writeObject(obj);
oos.close();
}
//回滚状态
public void load() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("obj.data")));
obj= (MementoObj) ois.readObject();
ois.close();
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Memento memento=new Memento();
memento.save();
memento.obj.setState("new");
System.out.println("新的状态" memento.obj.getState());
memento.load();
System.out.println("回归到旧的状态" memento.obj.getState());
}
}
迭代器
提供统一的遍历接口,保证调用者以相同的接口遍历各种数据结构的集合。
应用场景:各个集合对Iterator具体实现,比如ArrayList、HashMap等。
观察者
简单理解:当一个对象状态发生改变时,所有观察它的对象都被通知。
应用场景:发布-订阅
代码示例:
代码语言:java复制//观察者接口
public interface Observer {
void publish(Event event);
}
//观察事件
enum Event {
cry, hug
}
//具体观察者
class Mom implements Observer {
@Override
public void publish(Event event) {
System.out.println(event ",mom coming");
}
}
//具体观察者
class Dad implements Observer {
@Override
public void publish(Event event) {
System.out.println(event ",wait wait");
}
}
//被观察者
class Child {
List<Observer> observerList = new ArrayList<>();
//模拟注入观察者
{
observerList.add(new Mom());
observerList.add(new Dad());
}
//被观察者某个事件发生后通知观察者
public void action(Event event) {
for (Observer observer : observerList) {
observer.publish(event);
}
}
}
class ObserverMain {
public static void main(String[] args) {
Child child=new Child();
child.action(Event.cry);
}
}
模版方法
其核心思想是:定义一个操作的的一系列步骤,对于暂时不确定的步骤,交给子类实现就好。
代码语言:java复制public abstract class TrafficTemplate {
//定义骨架
final void goHome() {
int money = money();
int time = time();
System.out.println("花费" money "元," time "小时");
}
protected abstract int time();
protected abstract int money();
public static void main(String[] args) {
TrafficTemplate trafficTemplate = new Bicycle();
trafficTemplate.goHome();
}
}
代码语言:java复制public class Bicycle extends TrafficTemplate{
@Override
protected int time() {
return 2;
}
@Override
protected int money() {
return 0;
}
}
代码语言:java复制public class Car extends TrafficTemplate{
@Override
protected int time() {
return 1;
}
@Override
protected int money() {
return 5;
}
}
策略
定义一组算法并封装起来,运行时可以灵活的使用其中一个。其核心思想是讲容易变换的算法抽离出来作为“策略”参数传递。
应用场景:List.sort(Comparator) 其中Comparator可以看作策略。
代码示例:
代码语言:java复制//策略接口
public interface Strategy {
void toDO(AtomicInteger number);
}
代码语言:java复制//具体策略实现
public class PlusStrategy implements Strategy{
@Override
public void toDO(AtomicInteger number) {
number.incrementAndGet();
}
}
代码语言:java复制//具体策略实现
public class SubtractStrategy implements Strategy{
@Override
public void toDO(AtomicInteger number) {
number.decrementAndGet();
}
}
代码语言:java复制public class DoSomething {
@Override
public void toDO(AtomicInteger number,Strategy plusStrategy) {
plusStrategy.toDO(number);
}
}
代码语言:java复制 private static AtomicInteger number = new AtomicInteger();
public static void main(String[] args) {
DoSomething doSomething=new DoSomething();
//传入策略执行
doSomething.toDO(number,new SubtractStrategy());
System.out.println(number.get());
}