对设计模式认识比较肤浅,需要总结的比较通俗,可能部分失真,等以后随着实践和认知能力的提升,对文章再行完善。
目标
- 设计模式的六大原则
- 设计模式的划分
- 构建者模式
- 装饰器模式
- 工厂模式
1. 六大原则
1. 单一职责原则
Single Responsibility Principle, SRP,一个类只负责一个功能领域的相应职责。也就是我们常说的“高内聚,低耦合”
2. 开闭原则
Open-Closed Principle,OCP:对扩展开放,对修改关闭
也就是尽量在不修改原有代码的情况下进行扩展
3. 里式替换原则
Liskov Substitution Principle,LSP:所有引用父类的地方必须能透明的使用其子类的对象。
在程序中尽量使用基类类型来对对象进行定义,在运行时再确定子类类型,用子类对象来替换父类对象。
算是实现开闭原则的重要方式之一,通俗的说:子类可以扩展父类的功能,但不能改变父类原有的功能
4. 依赖倒置原则
Dependency Inversion Principle,DIP:抽象不应该依赖于细节,细节应该依赖于抽象,也就是面向接口编程,而不是针对实现编程。
开闭原则是目标,里式替换是基础,依赖倒置是手段。
感觉和spring的DI有点联系,后续再思考。
5. 接口隔离原则
Interface Segregation Principle,ISP:使用多个专门的接口,而不适用单一的总接口,即客户端不应该依赖那些它不需要的接口
每个接口应该承担相对独立的角色,提供定制服务,当然接口也不能太小,灵活性会变差。控制好接口的粒度。
6. 迪米特法则
Law of Demeter,LoD, 也叫最少知识原则,LeastKnowledge Principle,LKP:一个软件实体应当尽可能少的与其他实体发生相互作用
也就是解耦合,降低系统的耦合度。
2. 分类
大致按照设计模式的应用目标分类,分为创建型、结构型和行为型
- 创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory, Abstract Factory)、单例模式(Singleton)、构建者模式(Builder)、原型模式(Prototype)
- 结构型模式,是对软件设计结构的总结,专注于类、对象继承、组合方式的实践经验。常见的有桥接模式(Bridge)、适配器模式(Adapter)、装饰器模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、门面模式(Facade)、享元模式(Flyweight)等。
- 行为型模式,是从类或者对象之间交互、职责划分等角度总结的模式,常见有策略模式(Strategy)、解释器模式(Interpreter)、命令模式(Command)、观察者模式(Observer)、迭代器模式(Iterator)、模板方法模式(Template Method)、访问者模式(Visitor)
如果想快速了解各个模式的作用,可以参考 追MM与设计模式 https://blog.csdn.net/u011676417/article/details/70879632
3. 常用模式
1. 单例模式
之前总结有,https://blog.csdn.net/wjl31802/article/details/91360815
单例模式最常见,必须掌握
2. 装饰器模式
1. 典型使用场景
IO框架里面,InputStream是个抽象类,标准类库中提供了FileInputStream、ByteArrayInputStream等各种不同的子类,分别从不同角度对InputStream进行功能扩展。
2. 概念
本质上是包装同类型实例。我们对目标对象的调用,通过包装类覆盖过的方法,迂回调用被包装的实例,实现增加额外逻辑的目的,也就是“装饰”。
如BufferedInputStream为输入流增加了缓存。
优点:装饰类和被装饰类可以独立发展,不会耦合,是继承的一个替代模式
缺点:多层装饰较为复杂
使用场景:1. 扩展一个类的功能 2. 动态增加功能或撤销功能
3. 代码实现
为长方形和圆添加红边装饰
主要有几个类
接口
public interface Shape {
void draw();
}
实现类
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
创建抽象装饰类
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
@Override
public void draw() {
decoratedShape.draw();
}
}
创建实体装饰类
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
实例,用RedShapeDecorator装饰Shape对象
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
3. 构建者模式
1. 概念
比较优雅的解决构建复杂对象的麻烦,这里的“复杂”指的是类似需要输入的参数组合较多。如果用构造函数,需要为每一种可能的组合实现相应的构造函数,代码阅读星和可维护性差。
实质:从结构上把对象的使用逻辑和创建逻辑相互独立,隐藏对象实例的细节,为使用者提供更加规范、统一的逻辑。
典型的构造者模式,通常会被实现成fluent风格的API,也叫方法链。
以我们最常见的StringBuilder为例,append方法里面可能写String、char[ ]、int、long等等,该构建者为我们提供了统一的append方法,无需再写各种构造方法。非常方便。它的append后边可以再加其他方法,形成链。
2. 代码实现
public class Student {
private int id;
private String name;
private String password;
private String sex;
private String address;
// 构造器尽量缩小范围
private Student(){}
private Student(Student origin){
// 拷贝一份
this.id = origin.id;
this.name = origin.name;
this.password = origin.password;
this.sex = origin.sex;
this.address = origin.address;
}
// getter/setter
public static class Builder{
private Student target;
public Builder(){
target = new Student();
}
public Builder name(String name){
target.name = name;
return this;
}
public Builder password(String passwd) {
target.password = passwd;
return this;
}
public Builder sex(String sex) {
target.sex = sex;
return this;
}
public Builder address(String address) {
target.address = address;
return this;
}
public Student build(){
return new Student(target);
}
}
}
使用的话,
Student s = new Student.Builder().name("cc").password("123").sex("男").address("北京东长安街").build();
4. 工厂模式
最常见,创建对象的最佳方式,解耦
贴近实战,直接看spring的beanFactory工厂的诞生,自定义实现(也用到了单例模式)
public class MyBeanFactory {
// 定义一个properties对象
private static Properties properties;
// 静态代码块为对象赋值
static {
InputStream in = MyBeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
properties = new Properties();
try {
properties.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败");
}
}
public static Object getBean(String beanName){
Object bean = null;
// 根据beanName获取对象
String beanPath = properties.getProperty(beanName);
try {
// class.forName("全类名")获取class对象
bean = Class.forName(beanPath).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return bean;
}
}
这样的工厂貌似可以实现功能,但是不是单例模式,每次都得到有个船新?的对象,很显然不符合我们的期望,效率较低
public class MyBeanFactory {
// 定义一个properties对象
private static Properties properties;
// 用一个map容器来存储这些对象
private static Map<String,Object> beans;
// 静态代码块为对象赋值
static {
InputStream in = MyBeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
properties = new Properties();
try {
properties.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败");
}
// map进行初始化
beans = new HashMap<String, Object>();
// 获取properties里面的key
Enumeration<Object> keys = properties.keys();
while(keys.hasMoreElements()){
// 得到每一个key
String key = keys.nextElement().toString();
// 得到对应的beanPath
String beanPath = properties.getProperty(key);
Object value = null;
// 获取value的object
try {
value = Class.forName(beanPath).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 将key和value存到map中
beans.put(key,value);
}
}
}
5. 观察者模式
1. 概念
当对象间存在一对多关系时,使用观察者模式(Observer Pattern)。当一个对象(Observable)被修改,会自动通知它的依赖对象观察者(Observer)。
zookeeper就是观察者模式的典型代表
2. 相关API
java的API中提供Observable抽象类和Observer接口,构成对观察者模式的支持
1. java.util.Observable
被观察者,可将其子类化,未指定发送通知的顺序
常用方法:
- addObserver(Observer o) 向观察者集中添加观察者们
关键代码:类中有个set集合存放观察者们
// Adds an observer to the set of observers
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
- protected void setChanged() 标记此Observable对象为已改变的对象,hasChanged方法将返回true
- boolean hasChanged() 测试对象是否改变
- void notifyObservers(Object arg) 如果hasChanged方法指示对象已改变,通知所有观察者
- void deleteObserver(Observer o) 从观察者集合中删除某个观察者
2. Observer接口
观察者接口。可得到被观察者的更改通知
- void update(Observable o, Object arg) 只要改变了Observable对象就通知它
3. 代码实现
public class User implements Observer {
private String name; // 用户名
public User(String name){
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println("微信用户:"+name+",您订阅的公众号更新了以下内容:"+arg);
}
}
public class OfficialAccount extends Observable {
public void pushNewInfo(String info){
// 标记对象已改变
setChanged();
// 推送通知,需要先检测changed状态
notifyObservers(info);
}
}
public class Test {
public static void main(String[] args) {
User tom = new User("tom");
User jack = new User("jack");
User pony = new User("pony");
OfficialAccount officialAccount = new OfficialAccount();
officialAccount.addObserver(tom);
officialAccount.addObserver(jack);
officialAccount.addObserver(pony);
officialAccount.pushNewInfo("亲,非常抱歉,今天停更一天");
officialAccount.deleteObserver(tom);
officialAccount.pushNewInfo("明天早上不见不散哈,亲");
}
}