设计模式 -- 桥接模式(Bridge)
写在前面的话:读书破万卷,编码如有神
--------------------------------------------------------------------
主要内容包括:
- 初始桥接模式,包括: 定义、结构和说明、参考实现
- 体会桥接模式,包括: 场景问题、不用模式的解决方案、使用模式的解决方案
- 理解桥接模式,包括: 认识桥接模式、谁来桥接、典型例子-JDBC、广义桥接-Java中无处不在桥接、桥接模式的优缺点
- 思考桥接模式,包括: 桥接模式的本质、对设计原则的体现、何时选用
参考内容:
1、《研磨设计模式》 一书,作者:陈臣、王斌
---------------------------------------------------------------------
1、初始化桥接模式
1.1、定义
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
1.2、结构和说明
说明:
- Abstraction: 抽象部分的接口。通常在这个对象中,要维护一个实现部分的对象引用,在抽象对象里面的方法,需要调用实现部分的对象来完成。
- RefinedAbstraction:扩展抽象部分的接口。通常在这些对象中,定义根实际业务相关的方法,这些方法的实现通常会使用Abstraction中定义的方法。
- Implementor: 定义实现部分的接口。这个接口不用和Abstraction中的方法一致,通常是由Implementor接口提供基本的操作,而Abstraction里面定义的是基于这些基本操作的业务方法,也就是说Abstraction定义了基于这些基本操作的较高层次的操作。
- ConcreteImplementor: 真正实现Implementor接口的对象。
桥接模式通过引入实现的接口,把实现部分从系统中分离出去。那么,抽象这边持有一个具体的实现部分的实例就可以使用具体的实现了。
1.3、参考实现
/**
* 定义实现部分的接口,可以与抽象部分接口的方法不一样
*/
public interface Implementor { /**
* 实现抽象部分需要的某些具体功能
*/
public void operationImpl();
} /**
* 定义抽象部分的接口
*/
public abstract class Abstraction { //持有一个实现部分的对象
private Implementor impl; /**
* 构造方法
* @param impl 实现部分的对象
*/
public Abstraction(Implementor impl){
this.impl = impl;
} /**
* 实现一定的功能,可能需要转调实现部分的具体方法
*/
public void operation(){
this.impl.operationImpl();
}
} /**
* 真正的具体实现对象
*/
public class ConcreteImplementorA implements Implementor { @Override
public void operationImpl() {
//真正的实现
}
} /**
* 真正的具体实现对象
*/
public class ConcreteImplementorB implements Implementor { @Override
public void operationImpl() {
//真正的实现
}
} /**
* 扩充由Abstraction定义的接口功能
*/
public class RefinedAbstraction extends Abstraction { public RefinedAbstraction(Implementor impl) {
super(impl);
} /**
* 实现一定的功能
*/
public void otherOperation(){ }
}
---------------------------------------------------------------------
2、体会桥接模式
2.1、发送提示消息
考虑这样一个实际功能: 发送提示消息。从业务上看,提示消息分为:普通消息、加急消息、特急消息多种,不同的消息类型,业务功能处理是不一样的,比如加急消息是在消息上添加加急,而特急消息除了添加特急外,还会做一条催促的记录,多久不完成会继续催促; 从发送消息的手段上看,又有系统内短消息、手机短消息、邮件消息等。
2.2、不用模式的解决方案
(1)简化版本
先考虑一个简化版本,消息只发送普通消息,发送方式有:系统内短消息、邮件消息两种。
由于发送普通消息会有两种不同的实现方式,为了让外部统一操作,因此,把消息设计成接口,然后由两个不同的实现类分别实现系统内短消息方式和邮件发送消息的方式。
系统结构如下图:
示例代码:
/**
* 消息的统一接口
*/
public interface Message {
/**
* 发送消息
* @param message 要发送的消息内容
* @param toUser 消息发送的目的人员
*/
public void send(String message,String toUser);
} /**
* 以站内短消息的方式发送普通消息
*/
public class CommonMessageSMS implements Message { @Override
public void send(String message, String toUser) {
System.out.println("使用站内短消息的方式,发送消息: " + message + ",给"+toUser);
}
} /**
* 以Email的方式发送普通消息
*/
public class CommonMessageEmail implements Message{ @Override
public void send(String message, String toUser) {
System.out.println("使用Email消息的方式,发送消息: " + message + ",给"+toUser);
}
}
(2)实现发送加急消息
接着上面的实现,添加发送加急消息的功能,也有两种发送的方式,同样是站内短消息和Email的方式。(加急消息会自动在消息上添加加急,然后再发送消息,另外加急消息会提供监控的方法,让客户端可以随时查看加急消息的处理进度。因此加急消息需要扩展一个新的接口,除了基本的发送消息的功能,还需要添加监控的功能。)
系统结构如下图:
示例代码:
/**
* 加急消息的抽象接口
*/
public interface UrgencyMessage extends Message { /**
* 监控某消息的处理过程
* @param messageId 被监控的消息的编号
* @return
*/
public Object watch(String messageId);
} /**
* 以站内短消息的方式发送加急消息
*/
public class UrgencyMessageSMS implements UrgencyMessage { @Override
public void send(String message, String toUser) {
message = "[加急]:" + message;
System.out.println("使用站内短消息的方式,发送消息: " + message + ",给"+toUser);
} @Override
public Object watch(String messageId) {
//获取相应的数据,组织成监控的数据对象,然后返回
return null;
}
} /**
* 以站内Email的方式发送加急消息
*/
public class UrgencyMessageEmail implements UrgencyMessage { @Override
public void send(String message, String toUser) {
message = "[加急]:" + message;
System.out.println("使用Email消息的方式,发送消息: " + message + ",给"+toUser);
} @Override
public Object watch(String messageId) {
//获取相应的数据,组织成监控的数据对象,然后返回
return null;
}
}
2.3、有何问题
看了上面的实现,好像也能满足基本的功能要求,可是这么实现好不好呢?有没有什么问题呢?
下面继续添加新的功能:
(1)添加特急消息的处理(特急需要需要添加一个催促功能)
系统结构如下图:
观察上面的系统结构示意图,会发现一个很明显的问题,那就是通过这种继承的方式来扩展消息处理,会非常不方便。会看到在实现加急消息处理的时候,必须要实现站内短消息和Email两种处理方式;而在实现特急消息处理的时候,又必须实现站内短消息和Email这两种处理方式。
这就意味着,以后每扩展一下消息处理,都必须要实现这两种处理方式,是不是很痛苦?如果再要添加新的消息发送方式呢?
(2)添加发送手机消息的处理方式
根据目前的实现来看,如果要添加一种新的发送消息的方式,是需要在每一种抽象的具体实现中,都要添加发送手机消息的处理。也就是说,发送普通消息、加急消息、特急消息的处理,都可以通过手机来发送。
系统结构如下图:
把这个问题总结一下:采用通过继承来扩展的实现方式,有个明显的缺点,扩展消息的种类不太容易。不同种类的消息具有不同的业务,在这种情况下每个种类的消息,都需要实现所有不同的消息发送方式。更可怕的是,如果要加入新的消息发送方式或者新的消息类型的话,需要修改的地方太多了。
2.4、使用桥接模式来解决问题
(1)解决思路
分析上面的示例,可以看出示例的变化具有两个维度,一个维度是抽象的消息这边,包括:普通消息、加急消息、特急消息;另一个维度是在具体的消息发送方式上,包括:站内短消息、E-mail消息、手机消息。这两个维度一共可以组合出9种不同的可能性来。
它们的关系如下图:
(ps:现在出现问题的根本原因,就在于消息的抽象和实现是混杂在一起的,这就导致一个维度的变化会引起另一个维度进行相应的变化,从而使得程序扩展起来非常困难。)
要解决这个问题,就必须把这两个维度分开,也就是将抽象部分和实现部分分开,让他们相互独立,这样就可以实现独立变化了。
(2)示例代码
使用桥接模式来重写实现示例,首要任务就是要把抽象部分和实现部分分离出来,分析要实现的功能。抽象部分就是各个消息的类型所对应的功能,而实现部分就是各种发送消息的方式。
(a)先从简单的功能开始
消息类型:普通消息、加急消息; 发送方式: 站内短消息、E-mail。
使用桥接模式来实现这些功能的程序结构如下图:
示例代码如下:
(1)实现部分定义的接口
/**
* 实现发送消息的统一接口
*/
public interface MessageImplementor { /**
* 发送消息
* @param message 要发送的消息内容
* @param toUser 消息发送的目的人员
*/
public void send(String message,String toUser);
}
(2)抽象部分定义的接口
/**
* 抽象的消息对象
*/
public abstract class AbstractMessage { /**
* 持有一个实现部分的对象
*/
protected MessageImplementor impl; /**
* 构造方法
* @param impl
*/
public AbstractMessage(MessageImplementor impl){
this.impl = impl;
} /**
* 发送消息,转调实现部分的功能
* @param message 要发送的消息内容
* @param toUser 消息发送的目的人员
*/
public void sendMessage(String message,String toUser){
this.impl.send(message, toUser);
}
}
(3)站内短消息的实现
/**
* 以站内短消息的方式发送消息
*/
public class MessageSMS implements MessageImplementor { @Override
public void send(String message, String toUser) {
System.out.println("使用站内短消息的方式,发送消息: " + message + ",给"+toUser);
}
} (4)Email方式的实现
/**
* 以Email的方式发送消息
*/
public class MessageEmail implements MessageImplementor { @Override
public void send(String message, String toUser) {
System.out.println("使用Email消息的方式,发送消息: " + message + ",给"+toUser);
}
} (5)普通消息的实现
/**
* 普通消息的实现
*/
public class CommonMessage extends AbstractMessage { public CommonMessage(MessageImplementor impl) {
super(impl);
} @Override
public void sendMessage(String message, String toUser) {
//对于普通消息,什么都不干,直接调用父类的方法,把消息发送出去就可以了。
super.sendMessage(message, toUser);
}
}
(6)加急消息的实现
/**
* 加急消息
*/
public class UrgencyMessage extends AbstractMessage { public UrgencyMessage(MessageImplementor impl) {
super(impl);
} @Override
public void sendMessage(String message, String toUser) {
message = "[加急]:"+message;
super.sendMessage(message, toUser);
} /**
* 扩展新功能:监控某消息的处理过程
* @param messageId
* @return
*/
public Object watch(String messageId){
return null;
}
} (7)测试客户端
public class Client {
public static void main(String[] args) {
MessageSMS sms = new MessageSMS();
CommonMessage cm = new CommonMessage(sms);
UrgencyMessage um = new UrgencyMessage(sms);
cm.sendMessage("你有一个回复", "小明");
um.sendMessage("你有一个回复", "小明"); MessageEmail email = new MessageEmail();
CommonMessage cm2 = new CommonMessage(email);
UrgencyMessage um2 = new UrgencyMessage(email);
cm2.sendMessage("你有一个包裹", "小明");
um2.sendMessage("你有一个包裹", "小明");
}
} 运行结果:
使用站内短消息的方式,发送消息: 你有一个回复,给小明
使用站内短消息的方式,发送消息: [加急]:你有一个回复,给小明
使用Email消息的方式,发送消息: 你有一个包裹,给小明
使用Email消息的方式,发送消息: [加急]:你有一个包裹,给小明
(b)添加功能,消息类型增加:特急消息;发送方式增加:手机短信。
示例代码如下:
/**
* 特急消息
*/
public class SpecialUrgencyMessage extends AbstractMessage { public SpecialUrgencyMessage(MessageImplementor impl) {
super(impl);
} @Override
public void sendMessage(String message, String toUser) {
message = "[特急]:" + message;
super.sendMessage(message, toUser);
} public void hurry(String messageId){
//执行催促业务
}
} /**
* 手机短消息的方式发送消息
*/
public class MessageMobile implements MessageImplementor { @Override
public void send(String message, String toUser) {
System.out.println("使用手机短消息的方式,发送消息:" + message+",给 "+toUser);
}
} 客户端测试
public class Client {
public static void main(String[] args) {
MessageSMS sms = new MessageSMS();
CommonMessage cm = new CommonMessage(sms);
UrgencyMessage um = new UrgencyMessage(sms);
cm.sendMessage("你有一个回复", "小明");
um.sendMessage("你有一个回复", "小明");
System.out.println("-------------------------");
MessageEmail email = new MessageEmail();
CommonMessage cm2 = new CommonMessage(email);
UrgencyMessage um2 = new UrgencyMessage(email);
cm2.sendMessage("你有一个包裹", "小明");
um2.sendMessage("你有一个包裹", "小明");
System.out.println("-------------------------");
MessageMobile mobile = new MessageMobile();
CommonMessage cm3 = new CommonMessage(mobile);
UrgencyMessage um3 = new UrgencyMessage(mobile);
SpecialUrgencyMessage sum3 = new SpecialUrgencyMessage(mobile);
cm3.sendMessage("有人赞了你", "小东");
um3.sendMessage("有人赞了你", "小东");
sum3.sendMessage("有人赞了你", "小东");
}
} 运行结果:
使用站内短消息的方式,发送消息: 你有一个回复,给小明
使用站内短消息的方式,发送消息: [加急]:你有一个回复,给小明
-------------------------
使用Email消息的方式,发送消息: 你有一个包裹,给小明
使用Email消息的方式,发送消息: [加急]:你有一个包裹,给小明
-------------------------
使用手机短消息的方式,发送消息:有人赞了你,给 小东
使用手机短消息的方式,发送消息:[加急]:有人赞了你,给 小东
使用手机短消息的方式,发送消息:[特急]:有人赞了你,给 小东
---------------------------------------------------------------------
3、理解桥接模式
3.1、认识桥接模式
(1)什么是桥接
所谓桥接,通俗点时候就是在不同的东西之间搭一个桥,让它们能够连接起来,可以相互通讯和使用。那么在桥接模式中到底是给什么东西来搭桥呢?就是为被分离了的抽象部分和实现部分来搭桥,比如前面示例中的抽象的消息和具体消息发送之间搭个桥。
(ps: 在桥接模式中的桥接是单向的,也就是只能是抽象部分的对象去使用具体实现部分的对象,而不能反过来,也就是个单向桥。)
(2)为何需要桥接
为了达到让抽象部分和实现部分都可以独立变化的目的,在桥接模式中,是把抽象部分和实现部分分离开的,虽然从程序结构上是分开了,但是在抽象部分实现的时候,还是需要使用具体的实现的。所以需要搭个桥,让抽象部分通过这个桥就可以调用到实现部分的功能了,因此需要桥接。
(3)如何桥接
只要让抽象部分拥有实现部分的接口对象,就桥接上了,在抽象部分即可通过这个接口来调用具体实现部分的功能。也就是说,桥接在程序上体现了在抽象部分拥有实现部分的接口对象,维护桥接就是维护这个关系。
(4)独立变化
桥接模式的意图: 是使得抽象和实现可以独立变化,都可以分别扩充。也就是说抽象部分和实现部分是一种非常松散的关系。
(5)动态变换功能
由于桥接模式中的抽象部分和实现部分是完全分离的,因此可以在运行时动态组合具体的真实实现,从而达到动态变换功能的目的。
(6)桥接模式和继承
继承是扩展对象功能的一种常见手段,通常情况下,继承扩展的功能变化维护都是一维的,也就是变化的因素只有一类。
对于出现变化因素有两类的,也就是有两个变化纬度的情况,继承实现就会比较痛苦。比如上面的示例,就时有两个变化的纬度,一个是消息的类别,不同的消息类别处理不同;另外一个是消息的发送方式。
从理论上来说,如果用继承的方式来实现这种有两个变化纬度的情况,最后实际的实现类应该是两个纬度上可变数量的乘积那么多个。比如上面的示例,在消息类别的纬度上,目前的可变数量是3个:普通消息、加急消息、特急消息;在消息发送方式的纬度上,目前的可变数量也是3个: 站内短消息、Email消息、手机短消息。在这种情况下,如果要实现全的话,那么需要的实现类应该是 3 * 3 = 9个。如果要在任何一个纬度上进行扩展,都需要实现另一个纬度上的可变数量那么多个实现类,这也是为何会感到扩展起来困难。
桥接模式就是用来解决这种有两个变化纬度的情况下, 如何灵活的扩展功能的一个很好的方案。其实,桥接模式主要是把继承改成了使用对象组合,从而把两个纬度分开,让每一个纬度单独去变化,最后通过对象组合的方式,把两个纬度组合起来,每一种组合的方式就相当于原来继承中的一种实现,这样就有效的减少了实际实现的类的个数。
(7)桥接模式的调用顺序示意图
3.2、谁来桥接
所谓谁来桥接,就是谁来负责创建抽象部分和实现部分的关系,说得更直白点,就是谁来负责创建Implementor对象,并把它设置到抽象部分的对象中去。
大致有如下几种实现方式:
- 由客户端负责创建Implementor对象,并在创建抽象部分对象的时候,把它设置到抽象部分的对象中去。
- 可以在抽象部分对象构建的时候,由抽象部分的对象自己来创建相应的Implementor对象,可以给它传递一些参数,它可以根据参数来选择并创建具体的Implementor对象。
- 可以在Abstraction中选择并创建一个默认的Implementor对象,然后子类可以根据需要改变这个实现。
- 可以使用抽象工厂或者简单工厂来选择并创建具体的Implementor对象。
- 可以使用IoC/DI容器来创建具体的Implementor对象,并注入回到Abstraction中。
3.3、典型例子 -- JDBC
在Java应用中,对于桥接模式有一个非常典型的例子,就是: 应用程序使用JDBC驱动程序进行开发的方式。所谓驱动程序,指的是按照预先约定好的接口来操作计算机系统或者是外围设备的程序。
下面是JDBC操作的简单示例代码:
String sql = "具体要操作的sql语句"; //1:装载驱动
Class.forName("驱动的名称"); //2:创建连接
Connection conn = DriverManager.getConnection("连接数据库服务的URL","用户名","密码"); //3:创建statement或者是preparedStatement
PreparedStatement pstmt = conn.prepareStatement(sql); //4:执行sql,如果是查询,再获取ResultSet
ResultSet rs = pstmt.executeQuery(sql); //5:循环从ResultSet中把值取出来,封装到数据对象中去
while(rs.next()){
//取值示意,按名称取值
String uuid = rs.getString("uuid");
//取值示意,按索引取值
int age = rs.getInt(2);
} //6:关闭
rs.close();
pstmt.close();
conn.close();
我们写的应用程序,是面向JDBC的API在开发,这些接口相当于桥接模式中的抽象部分的接口。那么怎样得到这些API的呢?是通过DriverManager来得到的,此时的系统结构如下:
上面的JDBC的API,谁去实现呢? 光有接口,没有实现也不行的。现在就该驱动程序登场了,JDBC的驱动程序实现了JDBC的API,驱动程序就相当于桥接模式中的具体实现部分。而且不同的数据库,由于数据库实现不一样,可执行的sql也不完全一样,因此对于JDBC驱动的实现也是不一样的,也就是不同的数据库会有不同的驱动实现。此时驱动程序这边的程序结构如下图:
有了抽象部分--JDBC的API,有了具体的实现部分--驱动程序,那么它们如何连接起来呢?就是如何桥接呢? 它们是通过DriverManager来把它们桥接起来,从某个侧面来看,DriverManager在这里起到了类似于简单工厂的功能,基于JDBC的应用程序需要使用JDBC的API,如何得到呢?就通过DriverManager来获取相应的对象。此时系统的整体结构如下图:
通过上图可以看出,基于JDBC的应用程序,使用JDBC的API,相当于是对数据库操作的抽象扩展,算作桥接模式的抽象部分;而具体的接口实现是由驱动来完成的,驱动这边自然就相当于桥接模式的实现部分了。而桥接的方式,不再是让抽象部分持有实现部分,而是采用了类似于工厂的做法,通过DriverManager来把抽象部分和实现部分对接起来,从而实现抽象部分和实现部分解耦。
JDBC的这种架构,把抽象和具体分离开来,从而使得抽象和具体部分都可以独立扩展。
3.4、桥接模式的优缺点
优点:
- 分离抽象和实现部分,桥接分离了抽象部分和实现部分,从而极大地提高了系统的灵活性。让抽象部分和实现部分独立开来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。
- 更好的扩展性,由于桥接模式把抽象部分和实现部分分离开了,而且分别定义接口,这就使得抽象部分和实现部分可以分别独立地扩展,而不会相互影响。
- 可动态地切换实现,由于桥接模式把抽象部分和实现部分分离了,所以在实现桥接的时候,就可以实现动态的选择和具体的实现。
- 可减少子类的个数。
缺点:
3.5、思考桥接模式
(1)桥接模式的本质:分离抽象和实现。
桥接模式最重要的工作就是分离抽象部分和实现部分,这是解决问题的关键。只有把抽象部分和实现部分分离开了,才能够让它们独立地变化;只有抽象部分和实现部分可以独立地变化,系统才能有更好的扩展性和可维护性。
(2)对设计原则的体现
- 桥接模式很好地实现了开闭原则
- 桥接模式很好地体现了: 多用对象组合,少用对象继承
---------------------------------------------------------------------
---------------------------------------------------------------------
---------------------------------------------------------------------
---------------------------------------------------------------------
---------------------------------------------------------------------
---------------------------------------------------------------------
---------------------------------------------------------------------
设计模式 -- 桥接模式(Bridge)的更多相关文章
- 转:设计模式-----桥接模式(Bridge Pattern)
转自:http://www.cnblogs.com/houleixx/archive/2008/02/23/1078877.html 记得看原始链接的评论. 学习设计模式也有一段时间了,今天就把我整理 ...
- 大话设计模式--桥接模式 Bridge -- C++实现实例
1. 桥接模式: 将抽象部分与它的实现部分分离,使它们都可以独立的变化. 分离是指 抽象类和它的派生类用来实现自己的对象分离. 实现系统可以有多角度分类,每一种分类都有可能变化,那么把这种多角度分离出 ...
- 设计模式--桥接模式Bridge(结构型)
一.概述 在软件系统中,某些类型由于自身的逻辑,它具有两个或者多个维度的变化,如何应对这种"多维度的变化",就可以利用桥接模式. 引例: 设想如果要绘制矩形.圆形.椭圆.正方形,我 ...
- [工作中的设计模式]桥接模式bridge
一.模式解析: 策略模式一节讲过,通过扩展持有者,使持有者形成抽象类,然后实现多个具体持有者,策略模式可以转化为桥接模式. 桥接模式定义为:将抽象部分与实现部分分离,使它们都可以独立的变化,在软件系统 ...
- C#设计模式——桥接模式(Bridge Pattern)
一.概述在软件开发中,我们有时候会遇上一个对象具有多个变化维度.比如对汽车对象来说,可能存在不同的汽车类型,如公共汽车.轿车等,也可能存在不同的发动机,如汽油发动机.柴油发动机等.对这类对象,可应用桥 ...
- 乐在其中设计模式(C#) - 桥接模式(Bridge Pattern)
原文:乐在其中设计模式(C#) - 桥接模式(Bridge Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 桥接模式(Bridge Pattern) 作者:webabcd 介绍 ...
- 设计模式 -- 桥接模式(Bridge Pattern)
桥接模式 Bridge Pattern 结构设计模式 定义: 分离抽象部分和实现部分,使他们独立运行. 避免使用继承导致系统类个数暴增,可以考虑桥接模式. 桥接模式将继承关系转化为关联关系,减少耦合, ...
- 【设计模式】桥接模式 Bridge Pattern
开篇还是引用吕振宇老师的那篇经典的文章<设计模式随笔-蜡笔与毛笔的故事>.这个真是太经典了,没有比这个例子能更好的阐明桥接模式了,这里我就直接盗来用了. 现在市面上卖的蜡笔很多,各种型号, ...
- python 设计模式之桥接模式 Bridge Pattern
#写在前面 前面写了那么设计模式了,有没有觉得有些模式之间很类似,甚至感觉作用重叠了,模式并不是完全隔离和独立的,有的模式内部其实用到了其他模式的技术,但是又有自己的创新点,如果一味地认为每个模式都是 ...
随机推荐
- three.js为何如此奇妙
WebGL是在浏览器中实现三维效果的一套规范,而最初使用WebGL原生的API来写3D程序是一件非常痛苦的事情,在辛苦的付出下WebGL开源框架出现了,其中three.js就是非常优秀的一个,它掩盖了 ...
- 高性能优秀的服务框架-dubbo介绍
先来了解一下这些年架构的变化,下面的故事是我编的.... "传统架构":很多年前,刚学完JavaWeb开发的我凭借一人之力就开发了一个网站,网站 所有的功能和应用都集中在一起,方便 ...
- php菜刀分析学习
这里以eval为例 我们知道, php中的eval能把字符串当代码执行: eval('phpcode'); 注意, 这里的代码要有分号结尾, 我们测试: 我们创建一个最简单的SHELL: <?p ...
- 服务器部署之nginx的配置
nginx可作为Web和 反向代理 服务器,在高连接并发的情况下,Nginx是Apache服务器不错的替代品.下面记录一下自己对nginx的配置和使用. nginx的安装 环境:oracle-linu ...
- ProxySQL 排错 Max connect timeout reached while reaching hostgroup 10 after 10000ms
ProxySQL 排错 问题分析: 在ProxySQL在集群下,因未知原因导致误测到所有节点OFFLINE_HARD,并runtime_mysql_servers表清空,从而导致前端查询无法传递到后端 ...
- PlantUML——1.Hello
官网: http://www.plantuml.com/ 第一步: 下载 plantuml.jar文件: 第二步:创建一个demo.txt文件(与plantuml.jar在同一目录),内容如下: @s ...
- Prime
#include<iostream>#include<cstdio>#include<cstring>using namespace std; const int ...
- Jmeter中的变量(三)
变量(Variables) Jmeter中的变量(参数化)目的是为了提供改变请求变化的机制.比如登录场景,一般不能使用同一个账号做并发操作. 变量的特点 1) JMeter变量对于测试线程而言是局部变 ...
- Fiddler Web Session 列表(1)
Web Session 列表 位置: Web Session 列表 位于Fiddler界面的左侧 ,是Fiddler所抓取到的所有Session会话的列表集合. Web Session 列表 栏名词解 ...
- Python 读写xlsx
# pip install openpyxl # openpyxl只能用于处理xlsx,不能用于处理xlsfrom openpyxl import load_workbook # 打开文件ExcelF ...