将一个类的接口变成客户端期望的另一种接口
- 已经存在的类,他的方法和需求不匹配,
- 适配器模式不是软件设计阶段考虑的类,是由于随着软件的发展,不同产品,不同厂家功能类似、而接口不同的情况的解决方案。
总结:
适配器模式的局限性在于
1.原功能与期望的功能的输入参数和返回值要相同,即使不同也要有固定的关联关系
。
就像适配器经典例子中的电压一样,输入都是插头,输出都是电流。
类适配器采用继承的方式实现,暴漏了原方法给客户端,违法了最小职责原则,使用对象适配器能解决这一问题。
//原始类(已经存在的功能)
public class Origin {
//已经存在的方法
public int exitsMethod(){
return 1;
}
}
//目标类(开发的新功能)
public interface Target {
// 开发的新方法
public Boolean expectMethod(String args);
}
//测试
public class Test {
public static void main(String[] args) {
Adapter adapter = new Adapter();
Boolean abc = adapter.expectMethod("abc");
int i = adapter.exitsMethod(); //两个方法都暴漏给了客户端,不合适
}
}
// 适配器
public class Adapter extends Origin implements Target {
@Override
public Boolean expectMethod(String args) {
return exitsMethod() == 1;
}
}
代码:
//原始类(已经存在的功能)
public class Origin {
//已经存在的方法
public int exitsMethod(){
return 1;
}
}
//目标类(开发的新功能)
public interface Target {
// 开发的新方法
public Boolean expectMethod(String args);
}
// 适配器
public class Adapter implements Target {
private Origin origin;
public Adapter(Origin origin) {
this.origin = origin;
}
@Override
public Boolean expectMethod(String args) {
return origin.exitsMethod() == 1;
}
}
//测试
public class Test {
public static void main(String[] args) {
Adapter adapter = new Adapter(new Origin());
Boolean abc = adapter.expectMethod("abc");
}
}
需求:
现有登录方式:
- 用户名密码登录
需要新增如下第三方登录功能- 手机号验证码登录
- 微信登录
需要实现不同的厂商的同一登录功能。
//============= 现有功能代码===start=====
//现有的用户名密码登录
public class PasswordLoginService {
// 注册
public Boolean register(String userId,String password){
return true;
}
//用户密码登录
public Boolean login(String userId,String password){
return true;
}
}
//============= 现有功能代码===end=====
//新增第三方登录接口
public interface ThirdPartyLogin {
/** 手机号密码登录*/
Boolean loginByPhone(String phone,String code);
/**微信登录*/
Boolean loginWechat(String openId);
}
//适配器
public class PasswordForThirdPartyLoginAdapter extends PasswordLoginService implements ThirdPartyLogin{
@Override
public Boolean loginByPhone(String phone, String code) {
//缺少调用运营商校验验证码是否输入正确
return this.loginForRegister(phone,null);
}
@Override
public Boolean loginWechat(String openId) {
//缺少调用微信平台,校验openId
return this.loginForRegister(openId,null);
}
private Boolean loginForRegister(String userId,String password){
if(password == null){
password = "第三方登录,不需要密码";
}
if(super.register(userId,password)){
super.login(userId,password);
}
return true;
}
}
//测试
public class Test {
public static void main(String[] args) {
PasswordForThirdPartyLoginAdapter adapter = new PasswordForThirdPartyLoginAdapter();
adapter.login("用户命密码登录","密码");
adapter.loginByPhone("13822992932","372652");
adapter.loginWechat("wechatopenid");
}
}
版本一有一些缺陷
适配器中缺少第三方的一些校验代码逻辑,实际开发中如果将对接第三方的代码写在这个适配器中将会变得十分臃肿。
// 原有的密码登录
public class PasswordLoginService {
// 注册
public Boolean register(String userId,String password){
return true;
}
//用户密码登录
public Boolean login(String userId,String password){
return true;
}
}
public interface ThirdPartyLogin {
/** 手机号密码登录*/
Boolean loginByPhone(String phone,String code);
/**微信登录*/
Boolean loginWechat(String openId);
}
//登录适配器接口,所有的第三方登录实现改接口
public interface LoginAdapter {
Boolean support(LoginAdapter adapter);
Boolean login(String userId,LoginAdapter adapter);
}
//适配器(仅转接,不承接业务)
public class PasswordForThirdPartyLoginAdapter extends PasswordLoginService implements ThirdPartyLogin {
@Override
public Boolean loginByPhone(String phone, String code) {
return handleLogin(phone,new PhoneLoginAdapter());
}
@Override
public Boolean loginWechat(String openId) {
return handleLogin(openId,new WechatLoginAdapter());
}
private Boolean handleLogin(String userId,LoginAdapter loginAdapter){
if(loginAdapter.support(loginAdapter)){
return loginAdapter.login(userId,loginAdapter);
}
return false;
}
}
// 抽象登录适配器,作用:抽取公共的注册登录方法
public abstract class AbstractLoginAdapter extends PasswordLoginService implements LoginAdapter {
protected Boolean loginForRegister(String userId,String password){
if(password == null){
password = "第三方登录,不需要密码";
}
if(super.register(userId,password)){
super.login(userId,password);
}
return true;
}
}
//手机登录适配器
public class PhoneLoginAdapter extends AbstractLoginAdapter {
@Override
public Boolean support(LoginAdapter adapter) {
return adapter instanceof PhoneLoginAdapter;
}
@Override
public Boolean login(String userId, LoginAdapter adapter) {
return super.loginForRegister(userId,null);
}
}
//微信登录适配器
public class WechatLoginAdapter extends AbstractLoginAdapter {
@Override
public Boolean support(LoginAdapter adapter) {
return adapter instanceof WechatLoginAdapter;
}
@Override
public Boolean login(String userId, LoginAdapter adapter) {
return super.loginForRegister(userId,null);
}
}
//测试
public class Test {
public static void main(String[] args) {
PasswordForThirdPartyLoginAdapter adapter = new PasswordForThirdPartyLoginAdapter();
adapter.login("用户命密码登录","密码");
adapter.loginByPhone("13822992932","372652");
adapter.loginWechat("wechatopenid");
}
}
委派模式不属于GOF23种设计模式,是一种行为模式,与门面模式类似,不同的是委派模式是运行中
的行为处理逻辑。
委派模式更加注重委派的逻辑,而门面模式是一开始就定义好了的委托对象。
例如一个领导有很多下属,领导会跟进下属熟悉的领域进行安排工作,老板本身不参加具体事务。
委派模式能够将一个大型的任务拆分,更够通过管理子任务的执行情况解耦和提升效率。
委派模式与代理模式也有很多相似之处,区别如下:
代码
//员工接口
public interface Employee {
void doSomething(String task);
}
public class EmployeeA implements Employee{
@Override
public void doSomething(String task) {
System.out.println("我擅长写报告...");
}
}
public class EmployeeB implements Employee{
@Override
public void doSomething(String task) {
System.out.println("我擅长公关...");
}
}
// 领导跟进员工的特征安排任务
public class Leader implements Employee {
@Override
public void doSomething(String task) {
if("接待".equals(task)){
new EmployeeB().doSomething(task);
} else if("汇报".equals(task)){
new EmployeeA().doSomething(task);
}
}
}
// 测试,老板发话给领导安排任务,领导找到员工
public class Boos {
public void command(String task,Leader leader){
leader.doSomething(task);
}
public static void main(String[] args) {
new Boos().command("接待",new Leader());
}
}
是一种将数据结构与数据操作分离的设计模式
例如软件公司有若干码农,他们有一个共同的数据结构(姓名,KPI,bug数量,开发的功能,代码行数),对于不同的领导,如直属领导关注开发的功能,代码行数,开发部门经理关注程序员的bug数,公司CEO关注码农的KPI。
结合例子解释概念:
程序员的属性就是一个稳定的数据结构,公司的不同领导就是不同的访问者,他们关注的点(操作)各不相同。
- 数据结构稳定、作用与数据结构的操作经常变化的操作
- 需要数据结构和数据操作分离的场景
#(一 员工抽象)
public abstract class Employee {
//员工姓名
private String name;
//kpi分数
private int kpi;
public Employee(String name, int kpi) {
this.name = name;
this.kpi = kpi;
}
# 提供一个访问者进入的接口
public abstract void accept(Visitor visitor);
}
#(二 程序员实现)
public class Coder extends Employee {
public Coder(String name, int kpi) {
super(name, kpi);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* 产生的bug数
*/
public Integer bugNumbers(){
return new Random().nextInt(100);
}
}
#(三 产品经理实现)
public class Manager extends Employee{
public Manager(String name, int kpi) {
super(name, kpi);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* 经理考核上线的系统数量
* @return
*/
public int systemCount(){
return new Random().nextInt(10);
}
}
# 访问者接口
public interface Visitor {
// 访问员工
void visit(Coder employee);
* 访问经理
*/
void visit(Manager employee);
}
# 访问者实现一:CTO
public class CTOVisitor implements Visitor {
@Override
public void visit(Coder employee) {
System.out.println("CTO看"+employee.getName()+"的bug数"+employee.bugNumbers());
}
@Override
public void visit(Manager employee) {
System.out.println("CTO看"+employee.getName()+"的系统上线数"+employee.systemCount());
}
}
# 访问者实现二:CEO
public class CEOVisitor implements Visitor {
@Override
public void visit(Coder employee) {
System.out.println("CEO看"+employee.getName()+"的KPI"+employee.getKpi());
}
@Override
public void visit(Manager employee) {
System.out.println("CEO看"+employee.getName()+"的KPI"+employee.getKpi());
}
}
public class Test {
private static List<Employee> employeeList = new ArrayList();
static {
Coder coder = new Coder("张小开", 90);
Manager manager = new Manager("李经理", 90);
employeeList.add(coder);
employeeList.add(manager);
}
public static void main(String[] args) {
System.out.println("CTO===看员工");
CTOVisitor ctoVisitor = new CTOVisitor();
for (Employee employee : employeeList) {
employee.accept(ctoVisitor);
}
System.out.println("CEO=====看员工");
CEOVisitor ceoVisitor = new CEOVisitor();
for (Employee employee : employeeList) {
employee.accept(ceoVisitor);
}
}
}
优点:
缺点:
总结:
访问者模式是一种比较复杂的设计模式,使用场景较少,数据行为类设计模式。
是指由一个工厂根据不同的参数决定参加出那种产品。
- 需要创建的对象比较少
- 客户端只需要传入工厂类的参数,不需要关心创建对象的逻辑。
优点:只需要传入一个参数就可以获取想要的对象。
缺点:工厂类的职责过重,增加新的对象时需要修改创建工厂类的判断逻辑,违被了开闭原则
。
需求
根据不同的审批流程渠道获取申请人信息
实现代码:
public class CustomerSimpleFactory {
public static CustomerInterface getInstance(String taskType){
if("1".equals(taskType)){
return new MainCustomerService();
}else if("2".equals(taskType)){
return new EleCustomerService();
}else if("3".equals(taskType)){
return new PreCustomerService();
}else if("4".equals(taskType)){
return new QrCustomerService();
}
return null;
}
public static CustomerInterface getInstance2(Class<? extends CustomerInterface> clazz){
try {
return clazz.newInstance();
} catch (Exception e) {
}
return null;
}
}
定义一个工厂的接口,由子类实现工厂接口来决定创建的对象,工厂方法模式让类的实例推迟到子类工厂中
1.创建对象需要由大量的逻辑,(实例化目标类时逻辑比较复杂)
2.预计有较多的新实例需要加入
优点:
1.客户不需要知道类具体的创建细节 ,只需要关系返回的结果(与简单工厂一样有这个优点,似乎所有的工厂都是这个优点)
2.方便扩展新产品,符合开闭原则
缺点:
1.类的代码太多,增加了类的结构复杂度,2.增加了系统的抽象复杂度。
/**
* 工厂方法模式
*/
public interface CustomerFactory {
CustomerInterface getInstance();
}
/** 实现(一) 主流程 */
class MainCustomerFactory implements CustomerFactory{
@Override
public CustomerInterface getInstance() {
return null;
}
}
/** 实现(二) 预决策 */
class PreCustomerFactory implements CustomerFactory{
@Override
public CustomerInterface getInstance() {
return null;
}
}
class Test{
public static void main(String[] args) {
CustomerFactory factory = new MainCustomerFactory();
CustomerInterface instance = factory.getInstance();
}
}
指通过工厂创建一个具有产品,而同一个产品内有多个相互依赖或相关的接口。
比较绕口,举一个实际例子:总共有4个流程,其中每个流程都有获取申请人和担保人的方法。
优点:
1.具体产品在应用城代码隔离,无需关心具体创建细节
2.将一系列的产品主放在一起创建。(如:主流程申请人和主流程担保人在一起创建)
缺点:
1.产品中扩展新产品困难,需要修改抽象类,如新增一个联系人则需要修改抽象工厂方法,所有的子类都需要实现
2.类结构复杂,增加了系统的抽象性、复杂度。
我们代码实现定义中举的例子:
总共有4个流程,其中每个流程都有获取申请人和担保人的方法。
分析:
1.工厂目标:客户端需要根据自己的需要,获取4个不同的流程产品对象。
2. 每一个产品对象都分别由获取申请人信息和担保人信息的方法(申请人和担保人相关的接口)
//客户接口,以及不同流程的实现类
public interface Customer {
}
class MainCustomer implements Customer{
}
class PreCustomer implements Customer{
}
//担保人接口,以及不同流程的实现类
public interface Guarantor {
}
class MainGuarantor implements Guarantor{
}
class PreGuarantor implements Guarantor{
}
// 抽象工厂
public abstract class TaskFactory {
public void init(){
//一些初始化操作
}
public abstract Customer getCustomer();
public abstract Guarantor getGuarantor();
}
//主流程抽象工厂实现
class MainTaskFactory extends TaskFactory{
@Override
public Customer getCustomer() {
return new MainCustomer();
}
@Override
public Guarantor getGuarantor() {
return new MainGuarantor();
}
}
//预批流程工厂实现
class PreTaskFactory extends TaskFactory{
@Override
public Customer getCustomer() {
return new PreCustomer();
}
@Override
public Guarantor getGuarantor() {
return new PreGuarantor();
}
}
class Test {
public static void main(String[] args) {
TaskFactory factory = new MainTaskFactory();
Customer customer = factory.getCustomer();
Guarantor guarantor = factory.getGuarantor();
}
}
桥接模式又称双维度扩展模式,实际应用场景不太多。是将抽象部分与具体实现分离,使他们都可以独立变化。通过组合的方式将他们建立联系,而不是通过继承。
一个类存在两个或多个独立变化的维度,且这两个维度都需要进行独立扩展。
举例:发送通知的方式有邮件、短信两种方式,而通过发送通知的方式又有加急、普通。
优点:
1.分离抽象与具体
2.提高了系统的扩展性
3.复合开闭原则
缺点:
1.增加了系统的复杂度和设计难度
2.需要准确的识别系统中两个独立变化的维度(实际项目中难度较大)
上面的例子【发送通知的方式有邮件、短信两种方式,而通过发送通知的方式又有加急、普通。】实现代码
public interface Message {
void send(String message);
}
class EmailMessage implements Message{
@Override
public void send(String message) {
System.out.println("发送邮件信息:"+message);
}
}
class PhoneMessage implements Message{
@Override
public void send(String message) {
System.out.println("发送手机短信信息:"+message);
}
}
//抽象加急、普通短信桥接
public abstract class AbstractMessageBridge {
private Message message;
public AbstractMessageBridge(Message message){
this.message = message;
}
public void send(String message) {
this.message.send(message);
}
}
//普通短信
class NormalMessage extends AbstractMessageBridge{
public NormalMessage(Message message) {
super(message);
}
@Override
public void send(String message) {
super.send("[普通]"+message);
}
}
//加急短信
class UrgentMessage extends AbstractMessageBridge{
public UrgentMessage(Message message) {
super(message);
}
@Override
public void send(String message) {
super.send("[加急]"+message);
}
}
// 测试
class Test {
public static void main(String[] args) {
AbstractMessageBridge message = new UrgentMessage(new EmailMessage());
message.send("紧急休假");
AbstractMessageBridge message2 = new NormalMessage(new EmailMessage());
message2.send("休假");
AbstractMessageBridge message3 = new NormalMessage(new PhoneMessage());
message3.send("休假");
}
}
观察者模式又称发布订阅模式(Publish/Subscribe),定义一种一对多的依赖关系,一个主题对象可以被多个观察者同时监听,每当主题对象状态变化时,多依赖的对象都会被通知到。
- 当一个或多个对象的变化依赖另一个对象的变化
- 实现类似的广播机制,发布者无需知道具体的收听者,感兴趣的对象会自动接受该广播
public interface Subject<E> {
void addObserver(Observer<E> observer);
/**
* 通知对象
* @param event
*/
void notify(E event);
}
public class NodeCodeSubject implements Subject<String> {
private List<Observer<String>> observers = new ArrayList<>();
== 通知所有观察者
@Override
public void notify(String event) {
for (Observer<String> observer : observers) {
observer.update(event);
}
}
== 增加观察者的方法
@Override
public void addObserver(Observer<String> observer) {
observers.add(observer);
}
}
public interface Observer<E> {
/**
* 观察者对象变化
* @param event
*/
void update(E event);
}
public class NodeCodeObserver implements Observer<String>{
@Override
public void update(String event) {
System.out.println("监听到节点变化了nodeCode:"+event);
}
}
public class Test {
public static void main(String[] args) {
NodeCodeObserver nodeCodeObserver = new NodeCodeObserver();
NodeCodeObserver nodeCodeObserver2 = new NodeCodeObserver();
Subject subject = new NodeCodeSubject();
subject.addObserver(nodeCodeObserver);
subject.addObserver(nodeCodeObserver2);
subject.notify("end");
}
}
public class NodeCodeEvent {
@Subscribe
public void assignee(String nodeCode){
System.out.println("监听到nodeCode改变:"+nodeCode);
}
}
# 测试
public class Test {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
eventBus.register(new NodeCodeEvent());
eventBus.post("evl");
eventBus.post("visit");
}
}
监听节点的变化来分别处理不同的逻辑同时满足一个观察者可以监听多个节点的功能,分别实现如:刷新接口,人工派件等功能。
public class NodeInfo {
private Long caseId;
private String nodeCode;
// ignore getting/setting
}
继承 InitializingBean 为了结合Spring使用,提醒子类主动注册观察者
public interface NodeObserver extends InitializingBean {
/**
* 需要监听的节点
* @return
*/
List<String> registerCode();
/**
* 节点变化
* @param nodeInfo
*/
void nodeChange(NodeInfo nodeInfo);
}
public class NodeCodeManager {
//注册的程序通过Key,Map存储到这里
private static Map<String,List<NodeObserver>> map = new HashMap<>();
public static void register(NodeObserver observer){
for (String nodeCode : observer.registerCode()) {
List<NodeObserver> list = null;
if((list = map.get(nodeCode)) == null ){
list = new ArrayList<>();
}
list.add(observer);
map.put(nodeCode,list);
}
}
/**
* 节点变化,通知对应的节点
* @param nodeInfo
*/
public static void notifyAll(NodeInfo nodeInfo){
List<NodeObserver> list = map.get(nodeInfo.getNodeCode());
if(list != null){
for (NodeObserver nodeObserver : list) {
nodeObserver.nodeChange(nodeInfo);
}
}
}
}
public class RefreshCoreService implements NodeObserver {
@Override
public List<String> registerCode() {
// 注册这两个节点作为这个类的观察节点,实现同时观察指定的多个接口
return Arrays.asList("eval","vrf2");
}
@Override
public void nodeChange(NodeInfo nodeInfo) {
System.out.println("现在的节点是:"+nodeInfo.getNodeCode());
System.out.println("开始刷新接口.....");
}
@Override
public void afterPropertiesSet() throws Exception {
//主动注册本实现作为观察者
NodeCodeManager.register(this);
}
}
public abstract class AbstractNodeCodeObserver implements NodeObserver {
//这个抽象类的作用是帮助子类注册作为观察者,子类无需在重复定义
@Override
public void afterPropertiesSet() throws Exception {
NodeCodeManager.register(this);
}
}
@Service
public class AssigneeNodeObserver extends AbstractNodeCodeObserver {
@Override
public List<String> registerCode() {
return Arrays.asList("vrf","evl","visit");
}
@Override
public void nodeChange(NodeInfo nodeInfo) {
System.out.println("案件:caseId:"+nodeInfo.getCaseId()+"开始派件:"+nodeInfo.getNodeCode());
}
}
#测试,使用spring环境
@SpringBootTest
class SpbootApplicationTests {
@Resource
private AssigneeNodeObserver assigneeNodeObserver;
@Test
public void notifyAllTest() {
NodeInfo nodeInfo = new NodeInfo();
nodeInfo.setCaseId(1L);
nodeInfo.setNodeCode("vrf");
NodeCodeManager.notifyAll(nodeInfo);
}
}