编程是一门艺术
入坑程序猿到现在,每年都在学习设计模式,但每次看都会有新的感受。
先了解一下面向对象编程的六大原则:
开闭原理
扩展开放,修改关闭
里氏代换原则
任何基类出现的地方,子类一定可以出现
依赖倒转原则
依赖于抽象,不依赖于具体
接口隔离原则
使用多个隔离的接口,比使用单个接口要好
迪米特法则
最少知道原则,一个实体应尽量少的与其他实体产生联系
合成复用原则
多聚合,少继承
java包含23种设计模式,主要分为以下三大类:
- 1、创建型
- 它的主要功能是如何创建、组合和表示它的那些对象
- 2、结构型
- 组装现有的类,以及他们之间的关系
- 3、行为型
- 行为对象模式描述了一组对等的对象怎样相互协作以完成任务
1、创建型设计模式
单例模式:
单例模式保证构建对象的唯一性
,减少了内存消耗,但单例对象非抽象类,不利于扩展且多线程场景下需谨慎使用- 单例模式的使用场景非常广泛,例如Spring中的IOC容器,java日历控件中的Calendar.getInstance()方法等
public class Singleton
{
private static Singleton Instance;
private Singleton() {} //私有化构造
public static Singleton GetInstance()
{
if (Instance == null)
{
sync (Singleton.class)
{
if (Instance == null)
{
Instance = new Singleton();
} } }
return Instance; }}
原型模式:
- 原型模式:通过复制原型来创建新的对象,很像火影忍者中鸣人的隐分身术
原型模式中克隆有两种方式:1、深克隆 2.浅克隆
比如java Object类中的clone()方法,只复制了基础数据类型,属于浅克隆,而深度克隆包含了成员变量中引用数据类型的复制,要做到深度克隆,需重写clone()方法
工厂模式:
- 工厂模式建立了一个工厂类,对实现了同一接口的一些类进行实例的创建,举一个生活场景中的例子:我想吃鸡腿,去肯德基还是麦当劳由我决定,具体店铺的规模、员工数,我不关心,而肯德基&麦当劳即实现了同一个制造鸡腿的接口
工厂模式把创建对象和使用对象的方法进行了隔离,使用者无需关心创建的过程
好处:1、减少了出错率 2、将创建对象的过程进行封装,减少代码冗余 3、符合开闭原则
ex.spring 源码,AbstractApplicationContext对象使用了工厂模式
private static Calendar createCalendar(TimeZone zone, Locale aLocale)
{
Calendar cal = null;
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype == null) {
// Calendar type is not specified.
// If the specified locale is a Thai locale,
// returns a BuddhistCalendar instance.
if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
} else if (caltype.equals("japanese")) {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else if (caltype.equals("buddhist")) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
// Unsupported calendar type.
// Use Gregorian calendar as a fallback.
cal = new GregorianCalendar(zone, aLocale);
}
return cal;
- 如果说工厂模式构建的一组产品体系,那么抽象工厂更像是一组产品线,同样举例:我想造汽车,需要轮胎和发动机,现在有A代工厂和B代工厂都具备生产轮胎和发动机的功能,于是我可以选择一个代工厂进行制造
抽象工厂模式是对不同工厂进行多样化产品的组合
1、结构型设计模式
适配器模式:
将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作
- 适配器模式使用场景:
想使用一个已存在的类中的方法,但是该类不符合接口需求;或者需要创建一个可重用的类,适配没有提供合适接口的其它类。
适配器模式主要解决的问题就是我们要调用的接口类型,无法满足我们新系统的使用需求,这时候,我们需要将旧系统的接口,通过适配器进行转配,达到支持新接口调用的目的。
小故事
//被适配的对象(已存在对象)
public class Adapte {
public void adapteMethod() {
//do something
}
public void xxx() {
//do something
}
}
//目标对象接口
public interface Target {
void request();
}
//类适配器
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
super.adapteMethod();
}
}
//接口适配器
public class Adapter implements Target{
// 适配者是对象适配器的一个属性
private Adapte adapte = new Adaptee();
@Override
public void request() {
adapte.adapteMethod();
//...
}
}
main方法
public class Test {
public static void main(String[] args) {
//构建适配对象
Target target = new Adapter();
//调用重构后的方法
target.request();
}
}
装饰模式:
装饰模式的目的是增强被装饰对象方法的功能
- 装饰模式可以动态的扩展被装饰对象的功能
- 装饰对象与被装饰对象的关系是动态添加的且允许嵌套组合
小故事
1.我是一位明星,我需要衣物包装自己、需要学习更多的技能提升自己
抽象类(装饰、被装饰对象实现同一个接口)
public abstract class InputStream implements Closeable {
public abstract int read() throws IOException;
......
被装饰对象
public class FileInputStream extends InputStream
{
private static final ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<>();
private static boolean isRunningFinalize() {
Boolean val;
if ((val = runningFinalize.get()) != null)
return val.booleanValue();
return false;
}
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
装饰对象
public class BufferedInputStream extends FilterInputStream {
public BufferedInputStream(InputStream in) {
this(in, defaultBufferSize);
}
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
public class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
*/
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
main方法
public class Client {
public static void main(String[] args) {
//InputStream为抽象类,FileInputStream为被装饰类
InputStream inputStream = new FileInputStream("*.x");
//缓存流程
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
bufferedInputStream.read();
//压缩流
InputStream gZIPInputStream = new GZIPInputStream(inputStream);
gZIPInputStream.read();
}
}
ps:pxx-man举例
代理模式:
代理模式目的是代理原有对象
- 代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
小故事
1.我是一位明星,我需要衣物包装自己、需要学习更多的技能提升自己
抽象类
public interface Subject {
void visit();
}
被代理对象
public class RealSubject implements Subject {
private String name = "dosomething";
@Override
public void visit() {
System.out.println(name);
}
}
代理对象
public class ProxySubject implements Subject{
public ProxySubject() {
this.subject = new RealSubject();
}
@Override
public void visit() {
//do before
subject.visit();
//do after
}
}
main方法
public class Client {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.visit();
}
}
Java动态代理:
Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("proxyMethod".equals(method.getName())){
//
System.out.println("方法开始执行前处理");
method.invoke(proxy,args);
System.out.println("方法开始执行后处理");
}
return null;
}
}
);
为什么要用动态代理?
静态代理的缺点显而易见:
代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有委托类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。*
外观模式:
外观模式是为了解决类与类之家的依赖关系的
,通过组合的方式将方法放在一起执行
小故事
1.我是主机,我具备开启和关闭电脑的功能
桥接模式:
桥接模式就是把事物和其具体实现分开
,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化
本质上,桥接模式是将装饰模式的对象从构造器中抽取到了方法中
行为型设计模式
策略模式:
策略模式就是把算法行为抽象,调用方自行选择算法策略
,选择算法策略的前提是:调用方必须清楚每个算法的计算逻辑
小故事
1.京东plus会员价格
抽象算法
public interface MemberStrategy {
public double calcPrice(double booksPrice);
}‘
算法实现类
public class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("初级会员无折扣");
return booksPrice;
}
}
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("中级会员9折");
return booksPrice * 0.9;
}
}
public class AdvancedMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("高级会员2折");
return booksPrice * 0.2;
}
}
操作对象
public class Price {
//持有一个具体的策略对象
private MemberStrategy strategy;
public Price(MemberStrategy strategy){
this.strategy = strategy;
}
public double quote(double booksPrice){
return this.strategy.calcPrice(booksPrice);
}
}
main方法
public class Client {
public static void main(String[] args) {
//选择并创建需要使用的策略对象
MemberStrategy strategy = new AdvancedMemberStrategy();
//创建环境
Price price = new Price(strategy);
//计算价格
double quote = price.quote(300);
System.out.println("图书的最终价格为:" + quote);
}
}
观察者模式:
观察者模式本质就是发布订阅
,即一个对象状态改变,所有的依赖对象都将得到通知
订阅者通知行为分为两种:即时通知、定期扫描
小故事
- 微信朋友圈
- 航班延误通知
程序中使用观察者模式最广泛的就是MQ队列。
比如:
Broker数量或状态发生变更时,需要通知Topic更新对Borker组的监听状态
发布者
public class EventManager<E> {
// 监听器
protected CopyOnWriteArrayList<EventListener<E>> listeners = new CopyOnWriteArrayList<EventListener<E>>();
// 事件队里
protected BlockingQueue<Ownership> events;
// 线程名称
protected String name;
// 事件派发线程
protected Thread dispatcher;
// 事件派发处理器
protected EventDispatcher eventDispatcher = new EventDispatcher();
// 启动标示
protected AtomicBoolean started = new AtomicBoolean(false);
// 没有事件的也触发监听器
protected boolean triggerNoEvent;
// 事件的间隔(毫秒),并合并事件
protected long interval;
// 空闲时间
protected long idleTime;
// 获取事件超时时间
protected long timeout = 1000;
public EventManager() {
this(null, 0);
}
public EventManager(EventListener<E> listener) {
this(null, listener, 0);
}
public EventManager(String name) {
this(name, 0);
}
/**
* 开始
*/
public void start() {
if (started.compareAndSet(false, true)) {
// 清理一下,防止里面有数据
events.clear();
eventDispatcher.start();
if (name != null) {
dispatcher = new Thread(eventDispatcher, name);
} else {
dispatcher = new Thread(eventDispatcher);
}
dispatcher.setDaemon(true);
dispatcher.start();
}
订阅者
/**
* 主题消费者集群监听器,监听Topic上Broker组的变化
*/
protected class TopicClusterListener implements EventListener<ClusterEvent> {
@Override
public void onEvent(ClusterEvent event) {
if (!isStarted() || !topic.equals(event.getTopic())) {
return;
}
BrokerGroup group = event.getGroup();
switch (event.getType()) {
case ADD_BROKER:
// 能读数据
if (group.getPermission().contain(Permission.READ)) {
addGroupConsumer(group, event.getQueues());
}
break;
case REMOVE_BROKER:
removeGroupConsumer(group);
break;
.....
}
}
触发模式(定时)
/**
* 事件派发
*/
protected class EventDispatcher implements Runnable {
private AtomicBoolean started = new AtomicBoolean();
private AtomicBoolean gracefully = new AtomicBoolean(false);
private CountDownLatch latch;
public void start() {
if (started.compareAndSet(false, true)) {
latch = new CountDownLatch(1);
gracefully.set(false);
}
}
public void stop(boolean gracefully) {
if (started.compareAndSet(true, false)) {
this.gracefully.set(gracefully);
if (gracefully) {
try {
latch.await();
} catch (InterruptedException e) {
// 让当前线程中断
Thread.currentThread().interrupt();
}
}
}
}
/**
* 线程是否存活
*
* @return 存活标示
*/
protected boolean isAlive() {
return started.get() && !Thread.currentThread().isInterrupted();
}
@Override
public void run() {
long lastTime = SystemClock.getInstance().now();
long now;
Ownership event;
while (true) {
....
// 合并事件
if (interval > 0) {
// 获取当前所有事件
List<Ownership> currents = new ArrayList<Ownership>();
currents.add(event);
while (!events.isEmpty()) {
event = events.poll();
if (event != null) {
currents.add(event);
}
}
// 合并事件
publish(currents);
} else {
publish(event);
}
.....
}
其他应用场景,Zookeeper控制开关:
this.configCenterClient.registerConfigChangedListener(
topic, new ConfigChangedListener() {
@Override
public void handleDataChange(String key, String data) {
}
}
参考CSDN:
https://blog.csdn.net/weixin_39788083/article/details/89512076
责任链模式:
在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
责任链模式通过递归将任务向下传递
小故事
- 咚咚(请假),j-one(上线)审批流
- struts的拦截器
责任链基类
public abstract class Handler {
/**
* 持有后继的责任对象
*/
protected Handler successor;
public abstract void handleRequest();
/**
* 赋值方法,设置后继的责任对象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public boolean isPass();
}
责任链具体实现
public class ConcreteHandler extends Handler {
/**
* 处理方法,调用此方法处理请求
*/
@Override
public void handleRequest() {
//如果自己能通过,向下传递
if(!isPass()){
System.out.println("请求失败");
return;
}
//递归
if(getSuccessor() != null)
{
System.out.println("放过请求");
getSuccessor().handleRequest();
}else{
System.out.println("请求成功");
}
}
}
main方法
public class Client {
public static void main(String[] args) {
//组装责任链
Handler handler1 = new ConcreteHandler();
Handler handler2 = new ConcreteHandler();
handler1.setSuccessor(handler2);
//提交请求
handler1.handleRequest();
}
}
备忘录模式:
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
小故事
游戏读档、存档
备忘录模式有角色:
Client:发起方(相当于游戏中的owner)
Memento:备份信息(相当于游戏中的进度、血条等等)
CareTaker:管理者信息(相当于游戏云管家,由Client发起通知CareTaker进行读档、存档)
中介者模式:
中介者模式是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信
中介者顾名思义就是一个桥梁,通过中介者使对象间解耦。
● 具体中介者(ConcreteMediator)角色:实现了抽象中介者所声明的事件方法。具体中介者知晓所有的具体同事类,并负责具体的协调
各同事对象的交互关系。
小故事
租房、买房
Timer time = new Timer();
time.schedule(new TimerTask() {
@Override
public void run() {
//something1
}
},3000);
time.schedule(new TimerTask() {
@Override
public void run() {
//something2
}
},3000);
Timer充当的是中介者的角色,进行统一的协调处理,TimerTask充当的就是被协调的角色。