**开闭原则定义**: 一个软件实体 如类、模块和函数应该对扩展开放,修改关闭。所谓的开闭,也正是对扩展和修改两个行为的一个原则。强调的是用抽象构建框架,用实现扩展细节。可以提高软件的可复用性及可维护性,开闭原则,是面向对象设计中最基础的设计原则。它指导我们如何建立稳定灵活的系统,例如: 我们版本更新,我尽可能不修改源代码,但是可以增加新功能。
在现实生活中对开闭原则也有体现。比如,很多互联网公司都实行弹性制作息时间,规定每天工作8小时,意思就是说,对于每天工作8小时哥规定是关闭的,但是你什么时候来,什么时候走是开放的,早来早走,晚来晚走。
优点:提高软件系统的可复用性及可维护性
案例:
ICourse接口
public interface ICourse {
Integer getId();
String getName();
Double getPrice();
}
JavaCourse类
public class JavaCourse implements ICourse{
private Integer id;
private String name;
private Double price;
public JavaCourse(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
public Integer getId() {
return this.id;
}
public String getName() {
return this.name;
}
public Double getPrice() {
return this.price;
}
}
JavaDiscountCourse类
public class JavaDiscountCourse extends JavaCourse {
public JavaDiscountCourse(Integer id, String name, Double price) {
super(id, name, price);
}
public Double getDiscountPrice(){
return super.getPrice() * 0.6;
}
}
测试类
public class OpenCloseTest {
public static void main(String[] args) {
ICourse iCourse = new JavaDiscountCourse(1,"Java架构",11800D);
JavaDiscountCourse discountCourse = (JavaDiscountCourse)iCourse;
System.out.println("课程ID:" + discountCourse.getId() +
"\n课程标题:《" + discountCourse.getName() + "》" +
"\n原价:" + discountCourse.getPrice() +
"\n售价:" + discountCourse.getDiscountPrice());
}
}
类图
依赖倒置原则是指设计代码结构时,高层模块不应该依赖底层模块,二者都应该依赖其抽象;细节应该依赖抽象。通过依赖倒置,可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并能够降低修改程序所造成的风险。
优点: 可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险。
ICourse 接口:
public interface ICourse {
void study();
}
JavaCourse 类:
public class JavaCourse implements ICourse{
public void study() {
System.out.println("baby 在学习 Java 课程");
}
}
PythonCourse 类:
public class PythonCourse implements ICourse{
public void study() {
System.out.println("baby 在学习 Python 课程");
}
}
Baby类:
public class Baby {
public void study(ICourse course){
course.study();
}
}
调用实现
public class DipTest {
public static void main(String[] args) {
Baby baby = new Baby();
baby.study(new JavaCourse());
baby.study(new PythonCourse());
}
}
单一职责是指不要存在多于一个导致类变更的原因。假设我们有一个Class负责两个职责,一旦发生需求变更,修改其中的一个职责的逻辑代码,有可能会导致另一个职责的功能代码发生故障。这样一来,这个Class存在两个导致类变更的原因。如何解决这个问题呢?我们就要给两个职责分别用两个Class来实现,进行解耦。后期需求变更维护互不影响,这样的设计,可以降低类的复杂度,提高类的可读性,提高系统的可维护性,降低变更引起的风险。总体来说就是一个Class/Interface/Method只负责一项职责。
一个类只负责一个功能领域中的相应职责,就一个类而言,应该只有一个引起它变化的原因、是实现高内聚、低耦合的指导方针
解释:
高内聚
尽可能类的每个成员方法只完成一件事(最大限度的聚合)
模块内部的代码,相互之间的联系越强,内聚就越高,模块的独立性就越好
低耦合
减少类内部,一个成员方法调用另一个成员方法,不要有牵一发动全身
优点: 降低类的复杂度、提高类的可读性、提高系统的可维护性、降低变更引起的风险
接口隔离原则是指用多个专门的接口,而不使用单一的总接口,客户端不应该依赖他不需要的接口。这原则指导我们在设计接口时应当注意以下几点:
一个类对一类的依赖应该建在最小的接口之上。
使用多个隔离的接口,比使用单个接口要好,降低类之间的耦合度
尽量细化接口,接口中的方法尽量少(不是越少越好,一定要适度)
接口隔离原则符合我们常说的高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性。
最少知道原则,一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立
类之间的耦合度越低,就越有利于复用,一个处在松耦中的类一旦被修改,不会对关联的类造成太大波及
通过引入一个合理的第三者来降低现有对象之间的耦合度
任何基类可以出现的地方,子类一定可以出现
在程序中尽量使用基类类型来对对象进行定义,而在运行时在确定其子类型,用子类型对象来替换父类对象
引申含义:
子类可以实现父类的抽象方法,但不能改变父类原有的功能。
子类中可以增加自己特有的方法。
当子类的方法重载父类的方法时,方法的前置条件要比父类方法的输入参数更宽松。
当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出、返回值)要比父类更严格或相等。
工厂模式介绍:
它提供了一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式的实现方式
简单工厂模式
通过传入相关的类型来返回相应的类,这种方式比较单一,可扩展性相对较差。
工厂方法模式
通过实现类实现相应的方法来决定相应的返回结果,这种方式的扩展性比较强
工厂方法模式简介
核心组成
编码实践
/**
* 抽象工厂方法
*/
public interface PayFactory {
Pay getPay();
}
/**
* 具体产品工厂
*/
public class AliPayFactory implements PayFactory {
@Override
public Pay getPay() {
return new AliPay();
}
}
/**
* 抽象产品
*/
public interface Pay {
/**
* 统一下单
*/
void unifiedorder();
}
/**
* 具体产品
*/
public class AliPay implements Pay {
@Override
public void unifiedorder() {
System.out.println("支付宝支付 统一下单接口");
}
}
优点
缺点
抽象工厂模式
基于上述两种模式的扩展,且支持细化产品
应用场景
抽象工厂模式
单例设计模式:
这个是最简单的设计模式。单例意思只包含一个对象被称为单例的特殊类
通过单例模式可以保证系统中,应用该模式的类只有一个对象实例
使用场景
业务系统全局只需要一个对象实例,比如发号器、redis连接对象等
Spring IOC容器中的bean默认就是单例
Spring boot中Controller、Service、Dao层中通过@Autowired的依赖注入对象默认都是单例的
分类
懒汉: 就是所谓的懒加载,延迟创建对象
饿汉:与懒汉相反,提前创建对象
实现步骤
私有化构造函数
提供获取单例的方法
建造者模式
应用场景
场景举例
编码实战
/**
* 声明了建造者的公共方法
*/
public interface Builder {
/**
*细节方法
*/
void buildCpu();
void buildMainboard();
void buildDisk();
void buildPower();
void buildMemory();
Computer createComputer();
}
public class Director {
public Computer craete(Builder builder){
builder.buildMemory();
builder.buildCpu();
builder.buildMainboard();
builder.buildDisk();
builder.buildPower();
return builder.createComputer();
}
}
public class HighComputerBuilder implements Builder{
private Computer computer = new Computer();
@Override
public void buildCpu() {
computer.setCpu("高配 CPU");
}
@Override
public void buildMainboard() {
computer.setMainboard("高配 主板");
}
@Override
public void buildDisk() {
computer.setDisk("高配 磁盘");
}
@Override
public void buildPower() {
computer.setPower("高配 电源");
}
@Override
public void buildMemory() {
computer.setMemory("高配 内存");
}
@Override
public Computer createComputer() {
return computer;
}
}
优点
缺点
JDK源码的应用
适配器模式
常见的几类适配器
应用场景
编码实践
类适配器模式
//目标接口
interface Target
{
public void request();
}
//适配者接口
class Adaptee
{
public void specificRequest()
{
System.out.println("适配者中的业务代码被调用!");
}
}
//类适配器类
class ClassAdapter extends Adaptee implements Target
{
public void request()
{
specificRequest();
}
}
//客户端代码
public class ClassAdapterTest
{
public static void main(String[] args)
{
System.out.println("类适配器模式测试:");
Target target = new ClassAdapter();
target.request();
}
}
对象适配器
//对象适配器类
class ObjectAdapter implements Target
{
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee)
{
this.adaptee=adaptee;
}
public void request()
{
adaptee.specificRequest();
}
}
//客户端代码
public class ObjectAdapterTest
{
public static void main(String[] args)
{
System.out.println("对象适配器模式测试:");
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
桥接模式
桥接模式主要角色
应用场景
编码实践
public class BridgeTest {
public static void main(String[] args) {
Implementor imple = new ConcreteImplementorA();
Abstraction abs = new RefinedAbstraction(imple);
abs.Operation();
}
}
//实现化角色
interface Implementor {
public void OperationImpl();
}
//具体实现化角色
class ConcreteImplementorA implements Implementor {
public void OperationImpl() {
System.out.println("具体实现化(Concrete Implementor)角色被访问");
}
}
//抽象化角色
abstract class Abstraction {
protected Implementor imple;
protected Abstraction(Implementor imple) {
this.imple = imple;
}
public abstract void Operation();
}
//扩展抽象化角色
class RefinedAbstraction extends Abstraction {
protected RefinedAbstraction(Implementor imple) {
super(imple);
}
public void Operation() {
System.out.println("扩展抽象化(Refined Abstraction)角色被访问");
imple.OperationImpl();
}
}
装饰器模式
装饰器模式的定义
模式的结构
模式的优点
主要缺点
应用场景
编码实践
public class DecoratorPattern {
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
Component d = new ConcreteDecorator(p);
d.operation();
}
}
//抽象构件角色
interface Component {
public void operation();
}
//具体构件角色
class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
//抽象装饰角色
class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
//具体装饰角色
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
代理模式
代理模式的定义
模式的结构
动态代理
应用场景
编码实践
public class ProxyTest {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.Request();
}
}
//抽象主题
interface Subject {
void Request();
}
//真实主题
class RealSubject implements Subject {
public void Request() {
System.out.println("访问真实主题方法...");
}
}
//代理
class Proxy implements Subject {
private RealSubject realSubject;
public void Request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.Request();
postRequest();
}
public void preRequest() {
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest() {
System.out.println("访问真实主题之后的后续处理。");
}
}
责任链模式
责任链模式定义
模式的结构角色
应用场景
Tomcat对Encoding编码处理的处理,SpringBoot里面的拦截器、过滤器链
Netty里面的Handler处理器就是就是使用责任链模式
在请求处理者不明确的情况下向多个对象中的一个提交请求
如果有多个对象可以处理同一个请求,但是具体由哪个对象处理是由运行时刻动态决定的,这种对象就可以使用职责链模式
模式优点
模式缺点
不能保证每个请求一定被处理,由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理
对于较长的职责链,请求的处理可能涉及多个处理对象,系统性将受到一定影响
责任链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于责任链的错误设置而导致系统出错,如可能会造成循环调用
编码实践
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
//组装责任链
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
//提交请求
handler1.handleRequest("two");
}
}
//抽象处理者角色
abstract class Handler {
private Handler next;
public void setNext(Handler next) {
this.next = next;
}
public Handler getNext() {
return next;
}
//处理请求的方法
public abstract void handleRequest(String request);
}
//具体处理者角色1
class ConcreteHandler1 extends Handler {
public void handleRequest(String request) {
if (request.equals("one")) {
System.out.println("具体处理者1负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
//具体处理者角色2
class ConcreteHandler2 extends Handler {
public void handleRequest(String request) {
if (request.equals("two")) {
System.out.println("具体处理者2负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
迭代器模式
迭代器模式的定义
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示,迭代器模式是一种对象行为模式
应该是Java中应用最多的设计模式
提到迭代器,想到它是与集合相关的,集合也叫容器,可以将集合看成是一个可以包容对象的容器,例如List,Set,Map,甚至数组都可以叫做集合,迭代器的作用就是把容器中的对象一个一个地遍历出来
模式的结构角色
模式优点
模式缺点
增加了类的个数,在一定程度上增加了系统的复杂性
迭代器模式在遍历的同时更改迭代器所在的集合结构会导致出现异常
编码实践
public class IteratorPattern {
public static void main(String[] args) {
Aggregate ag = new ConcreteAggregate();
ag.add("中山大学");
ag.add("华南理工");
ag.add("韶关学院");
System.out.print("聚合的内容有:");
Iterator it = ag.getIterator();
while (it.hasNext()) {
Object ob = it.next();
System.out.print(ob.toString() + "\t");
}
Object ob = it.first();
System.out.println("\nFirst:" + ob.toString());
}
}
//抽象聚合
interface Aggregate {
public void add(Object obj);
public void remove(Object obj);
public Iterator getIterator();
}
//具体聚合
class ConcreteAggregate implements Aggregate {
private List
观察者模式
观察者模式定义
模式角色
模式的优点
降低了目标与观察者之间的耦合关系,两者之间时抽象耦合关系,符合依赖倒置原则
目标与观察者之间建立了一套触发机制
模式的缺点
观察者和观察目标之间有循环依赖的话,会触发它们之间进行循环调用,可能导致系统奔溃
当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率
应用场景
编码实践
public class ObserverPattern {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
//抽象目标
abstract class Subject {
protected List observers = new ArrayList();
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
//具体目标
class ConcreteSubject extends Subject {
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for (Object obs : observers) {
((Observer) obs).response();
}
}
}
//抽象观察者
interface Observer {
void response(); //反应
}
//具体观察者1
class ConcreteObserver1 implements Observer {
public void response() {
System.out.println("具体观察者1作出反应!");
}
}
//具体观察者1
class ConcreteObserver2 implements Observer {
public void response() {
System.out.println("具体观察者2作出反应!");
}
}
状态模式
状态模式的定义
模式的结构角色
模式的优点
模式的缺点
状态模式的使用必然会增加系统的类与对象的个数
状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码混乱
状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改哪些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对象类的源码。
应用场景
一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为
代码中包含大量与对象状态有关的条件语句,比如一个操作中含有庞大的多分支的条件if else语句,且这些分支依赖于该对象的状态
电商订单状态: 未支付、已支付、派送中、收货完成等状态,各个状态下处理不同的事情
编码实践
public class StatePatternClient {
public static void main(String[] args) {
Context context = new Context(); //创建环境
context.Handle(); //处理请求
context.Handle();
context.Handle();
context.Handle();
}
}
//环境类
class Context {
private State state;
//定义环境类的初始状态
public Context() {
this.state = new ConcreteStateA();
}
//设置新状态
public void setState(State state) {
this.state = state;
}
//读取状态
public State getState() {
return (state);
}
//对请求做处理
public void Handle() {
state.Handle(this);
}
}
//抽象状态类
abstract class State {
public abstract void Handle(Context context);
}
//具体状态A类
class ConcreteStateA extends State {
public void Handle(Context context) {
System.out.println("当前状态是 A.");
context.setState(new ConcreteStateB());
}
}
//具体状态B类
class ConcreteStateB extends State {
public void Handle(Context context) {
System.out.println("当前状态是 B.");
context.setState(new ConcreteStateA());
}
}
策略模式
策略模式定义
模式的结构角色
模式的优点
模式的缺点
客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类
策略模式可以造成很多的策略类,增加维护难度
应用场景
如果在一个系统里面有许多类,它们之间的区别仅在于行为,那么可以使用策略模式(例如支付方式)
不希望暴露复杂的,与算法有关的数据结构,那么可以使用策略模式来封装算法
一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中
编码实践
public class StrategyPattern {
public static void main(String[] args) {
Context c = new Context();
Strategy s = new ConcreteStrategyA();
c.setStrategy(s);
c.strategyMethod();
System.out.println("-----------------");
s = new ConcreteStrategyB();
c.setStrategy(s);
c.strategyMethod();
}
}
//抽象策略类
interface Strategy {
public void strategyMethod(); //策略方法
}
//具体策略类A
class ConcreteStrategyA implements Strategy {
public void strategyMethod() {
System.out.println("具体策略A的策略方法被访问!");
}
}
//具体策略类B
class ConcreteStrategyB implements Strategy {
public void strategyMethod() {
System.out.println("具体策略B的策略方法被访问!");
}
}
//环境类
class Context {
private Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void strategyMethod() {
strategy.strategyMethod();
}
}
模板模式
模板模式定义
模式的结构
模板模式的优点
模板模式的缺点
对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计由更加抽象,间接地增加了系统实现的复杂度
父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度
由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有的子类都要改一遍
应用场景
算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现
当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码重的不同之处,并且将不同之处分离为新的操作,最后,用一个调用这些新的操作的模板方法来替换这些不同的代码
当需要控制子类的扩展使,模板方法只在特定点调用钩子 操作,这样就只允许在这些点进扩展
代码实践
public class HookTemplateMethod {
public static void main(String[] args) {
HookAbstractClass tm = new HookConcreteClass();
tm.TemplateMethod();
}
}
//含钩子方法的抽象类
abstract class HookAbstractClass {
//模板方法
public void TemplateMethod() {
abstractMethod1();
HookMethod1();
if (HookMethod2()) {
SpecificMethod();
}
abstractMethod2();
}
//具体方法
public void SpecificMethod() {
System.out.println("抽象类中的具体方法被调用...");
}
//钩子方法1
public void HookMethod1() {
}
//钩子方法2
public boolean HookMethod2() {
return true;
}
//抽象方法1
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
//含钩子方法的具体子类
class HookConcreteClass extends HookAbstractClass {
public void abstractMethod1() {
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2() {
System.out.println("抽象方法2的实现被调用...");
}
public void HookMethod1() {
System.out.println("钩子方法1被重写...");
}
public boolean HookMethod2() {
return false;
}
}
操作,这样就只允许在这些点进扩展
代码实践
public class HookTemplateMethod {
public static void main(String[] args) {
HookAbstractClass tm = new HookConcreteClass();
tm.TemplateMethod();
}
}
//含钩子方法的抽象类
abstract class HookAbstractClass {
//模板方法
public void TemplateMethod() {
abstractMethod1();
HookMethod1();
if (HookMethod2()) {
SpecificMethod();
}
abstractMethod2();
}
//具体方法
public void SpecificMethod() {
System.out.println("抽象类中的具体方法被调用...");
}
//钩子方法1
public void HookMethod1() {
}
//钩子方法2
public boolean HookMethod2() {
return true;
}
//抽象方法1
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
//含钩子方法的具体子类
class HookConcreteClass extends HookAbstractClass {
public void abstractMethod1() {
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2() {
System.out.println("抽象方法2的实现被调用...");
}
public void HookMethod1() {
System.out.println("钩子方法1被重写...");
}
public boolean HookMethod2() {
return false;
}
}