Hello~ 大家好,欢迎来到蜗牛君漫聊设计模式系列。本篇文章是蜗牛君漫聊设计模式系列的第一篇文章,若有不足之处还望大家见谅,与此同时大家也可以评论区留言,蜗牛君积极改正。今天要讲的是代理模式中的静态代理。正所谓艺术来源于生活而高于生活,代理模式同样也来源于生活又高于生活。接下来让我们通过一个现实生活中的例子来感受一下代理模式的神奇吧。
01
大家作为职场人士都会有一些共同的社会属性,比如缴纳社保。对于蜗牛君这样的北漂来说社保的重要性不言而喻,连续缴纳满5年才会有摇号和买房的资格,能不能摇到号,能不能买得起房另当别论,但是能够拥有这样的资格还是很让人期待的。当然现在很多科技公司具备能够办理工作居住证的资质,但前提是你能够进入一家这样的公司,更何况像蜗牛君这样不断失业的务工人员,有时候需要想办法在失业的情况下续上社保缴纳的记录。作为一个失业人员是无法自己给自己缴纳社保的,社保的缴纳只能通过企业的途径进行操作。那有什么办法呢?第一反应当然是向神奇的某宝求助了。是的,没错,就是在某宝上寻找可以代缴社保的公司。而这整个过程也体现出了我们设计模式当中代理模式的理念。
设计模式定义
所谓代理模式,就是为其他对象提供一种代理,以控制对这个对象的访问。这就像我们在某宝上找的代缴公司为我们提供代理服务,当我们的社保信息挂靠到该公司之后,如果不做减员其他公司是无法为我们缴纳社保的,这也就是对我们社保信息的控制。
代理模式有三大核心要素
抽象角色:抽象角色可以是抽象类也可以是接口,是一个最普通的业务类型定义。
具体角色:也是被代理角色,是具体的业务逻辑执行者。
代理角色:也叫代理者,负责对被代理角色的使用,它把抽象角色定义的所有方法委托给具体角色实现,并在具体角色执行完毕的前后做预处理和善后工作;
如何理解这三大元素呢?
抽象角色,就是我们作为职场人士所共有的属性,比如缴纳社保。
具体角色,就是我们个人,由于我们所在的公司不同因此福利待遇也会不同,有的公司不给缴纳社保(这是骗子公司,大家不要去!),有的给缴纳三险一金,有的给足额缴纳五险一金,更好的公司甚至给缴纳七险一金,这就是具体角色的定义。
代理角色,也就是为我们缴纳社保的公司,他在为我们缴纳社保前会从我们的工资里面扣除相应的金额用来缴纳社保个人部分。
三个核心要素如何用代码表示?
// 抽象角色
public interface AbstractObject {
void doSomething();
}
// 具体角色
public class RealObject implements AbstracObject {
@Overside
public void doSomething() {
// 需要处理的具体业务逻辑
}
}
// 代理角色
public class ProxyObject implements AbstracObject {
private AbstracObject object;
// 创建代理角色时,传递具体角色进来,这个具体角色就是要被代理的对象
public ProxyObject(AbstracObject object) {
this.object = object;
}
// 具体业务处理方法
@Overside
public void doSomething() {
before();
subject.doSomething();
after();
}
// 预处理方法
private void before() {
// do something ...
}
// 善后方法
private void after() {
// do something ...
}
}
// 具体的使用场景
public class client {
public static void main(String[] args) {
// 创建具体对象
RealObject realObject = new RealObject();
// 创建代理对象,并将被代理对象传递到内部
ProxyObject proxyObject = new ProxyObject(realObject);
// 代理主题执行业务逻辑处理方法
proxyObject.doSomething();
}
}
以上就是高度抽象之后的代理模式,但也是最简单的模型。其实这里面有两点很关键,能够理解这两点,那代理模式的精髓你就真正的学到了。
第一点,我们作为独立的个人无法直接给自己缴纳社保,必须通过公司的途径进行个人社保的缴纳。
第二点,公司进行缴纳时最终还是给我们自己的个人账户进行付款并且钱还是我们自己出,这就说明了代理对象为什么要实现和具体对象同样的接口或抽象类,最终的支付还是要调用具体对象的方法,而代理对象自己的方法不涉及任何关于账户的操作。以上两点就是代理模式的精髓。
代理模式的类别
最简版代理模式
- 静态代理
- 普通代理:我们必须知道代理类的存在,然后才能访问,不必知道具体角色的存在;在使用上的描述是:调用者只能访问代理角色,不能直接访问具体角色,具体角色的产生由代理角色完成。
- 强制代理:我们必须知道具体角色的存在,不必知道代理角色的存在,代理角色的产生由真实角色完成;从另一个方面说明,真实角色必须指定自己的代理角色。
多元化代理模式:本身代理角色的存在就是为了增强真实角色,对真实角色进行扩展和过滤;因此代理角色还可以实现其他的接口,加入更多的功能和场景。
- 动态代理:核心点是实现阶段不需要关心要代理谁,而在运行阶段才指定代理哪个类。
代理模式的优点
- 职责清晰:具体角色只需要关系自身的业务逻辑实现就可以了,与核心业务无关的代码逻辑通过代理主题角色来实现就可以了。
- 高扩展性:具体角色时常会变更,但是只要实现了抽象角色这个接口,代理角色就可以在毫无改动的情况下进行使用。
- 智能化:这点讲到动态代理时就能体会到了。
02
最简版代理模式
蜗牛君失业了,但是不想断掉坚持了多年的社保缴费记录,可眼下又没有找到工作,只能从某宝上找个代缴公司了。
1、创建员工抽象角色,添加公共属性
/**
* 员工属性接口
*/
public interface IEmployee {
// 入职公司,将个人信息进行挂靠
void entry(String idCard);
// 缴纳社保
void paySocialSecurityCost(int cost);
}
2、创建具体角色,实现具体逻辑操作
/**
* 员工
*/
public class Employee implements IEmployee {
// 员工姓名
private String name;
public Employee(String name) {
this.name = name;
}
/**
* 登录个人社保缴费账户
* @param idCard
*/
@Override
public void entry(String idCard) {
// do Something ...
System.out.println("<----------------------------");
System.out.println("进入个人社保缴费系统");
System.out.println("提示:【身份ID:" + idCard + ",姓名:" + name + ",已进入社保缴费系统!】");
System.out.println("---------------------------->");
}
/**
* 缴纳社保费用
*/
@Override
public void paySocialSecurityCost(int cost) {
// do Something ...
System.out.println("<----------------------------");
System.out.println("开始社保缴费流程:");
System.out.println("1.缴纳养老保险");
System.out.println("2.缴纳失业保险");
System.out.println("3.缴纳医疗保险");
System.out.println("4.缴纳工伤保险");
System.out.println("5.缴纳生育保险");
System.out.println("提示:【姓名:" + name + ",本月社保已缴纳,共支付:" + cost + "元】");
System.out.println("---------------------------->");
}
}
3、创建代理角色,也就是代理公司角色
/**
* 代理公司
*/
public class ProxyCompany implements IEmployee {
private IEmployee employee;
/**
* 构造方法,传入需要代理缴费服务的员工对象
* @param employee
*/
public ProxyCompany(IEmployee employee) {
this.employee = employee;
}
/**
* 将客户信息挂靠到公司下面,进入客户缴费账户
*
* @param idCard
*/
@Override
public void entry(String idCard) {
employee.entry(idCard);
}
/**
* 代理对象支付社保费用
*/
@Override
public void paySocialSecurityCost(int cost) {
employee.paySocialSecurityCost(cost);
}
}
4、使用场景
又到了缴纳社保的时候了,这是失业后第一次通过这个代缴公司给自己缴纳社保。
public class Client {
public static void main(String[] args) {
// 1、创建失业员工---蜗牛君
Employee employee = new Employee("蜗牛君");
// 2、某宝上挑选一家社保代缴公司---XXX公司
ProxyCompany proxyCompany = new ProxyCompany(employee);
// 3、将蜗牛君的身份证号给代理公司,代理公司将个人信息进行挂靠,并进入账户
proxyCompany.entry("1234567890");
// 4、将社保费用打给代理公司
proxyCompany.paySocialSecurityCost(1700);
}
}
Log日志
<----------------------------
进入个人社保缴费系统
提示:【身份ID:1234567890,姓名:蜗牛君,已进入社保缴费系统!】
---------------------------->
<----------------------------
开始社保缴费流程:
1.缴纳养老保险
2.缴纳失业保险
3.缴纳医疗保险
4.缴纳工伤保险
5.缴纳生育保险
提示:【姓名:蜗牛君,本月社保已缴纳,共支付:1700元】
---------------------------->
总结:
怎么样,是不是很简单?这就是蜗牛君失业后第一次通过代理公司进行个人社保的缴纳。同时也是代理模式最简单也最核心的表述。
03
普通代理模式
上面部分讲的是最简单的模式,可能应用的地方并不多,因为实际的业务会很复杂,这种模式并不能够很好的应用到实际项目中,下面让我们看看普通代理模式,究竟有何普通之处。
剧情来了,自从蜗牛君失业后就陷入了困境之中,投出去的简历没有任何回复,转眼间一个月过去了,又要缴纳社保了,没有收入也就罢了,还要自己掏钱缴纳社保。算了,老老实实掏钱吧。
1、具体角色的改动
蜗牛君还是那个蜗牛君,社会属性没有改变,因此抽象角色和具体角色的业务方法和属性都无需改动,需要改动的是代理角色。因为蜗牛君已经不是第一次在这个代缴公司付费了,因此这家代缴公司已经有了我的记录,我的个人信息已经挂靠到这家公司下面了。这里还要增加一个场景,蜗牛君要去找上次选择的那家代理公司,钱可不能打错地方啊!所以具体角色做出一点点的改变,加一个判断:
public class Employee implements IEmployee {
private String name;
public Employee(IEmployee proxyCompany, String name) {
System.out.println(proxyCompany.getClass().getSimpleName());
if (proxyCompany == null || !proxyCompany.getClass().getSimpleName().equals("ProxyCompany")) {
throw new Error("这不是我在某宝上找的代理公司!!!");
}
this.name = name;
}
/**
* 登录个人社保缴费账户
* @param idCard
*/
@Override
public void entry(String idCard) {
// do Something ...
System.out.println("<----------------------------");
System.out.println("进入个人社保缴费系统");
System.out.println("提示:【身份ID:" + idCard + ",姓名:" + name + ",已进入社保缴费系统!】");
System.out.println("---------------------------->");
}
/**
* 缴纳社保费用
*/
@Override
public void paySocialSecurityCost(int cost) {
// do Something ...
System.out.println("<----------------------------");
System.out.println("开始社保缴费流程:");
System.out.println("1.缴纳养老保险");
System.out.println("2.缴纳失业保险");
System.out.println("3.缴纳医疗保险");
System.out.println("4.缴纳工伤保险");
System.out.println("5.缴纳生育保险");
System.out.println("提示:【姓名:" + name + ",本月社保已缴纳,共支付:" + cost + "元】");
System.out.println("---------------------------->");
}
}
2、代理角色的改动
代理公司对于一个已经挂靠到公司名下的客户再次续费的时候该做出什么变动呢?请看代码:
public class ProxyCompany implements IEmployee {
private IEmployee employee;
/**
* 给自己公司名下的员工缴纳社保
* @param name
*/
public ProxyCompany(String name) {
// 通过名字找到自己的客户
employee = new Employee(this, name);
}
// 其他代码不变
}
3、使用场景的改动
蜗牛君要打钱了,好心疼,好惨。
public class Client {
public static void main(String[] args) {
// 社保要续费了,找到我在某宝上签约的代理公司;
// 因为我已经挂靠到代理公司的名下了,因此直接呼叫代理公司
ProxyCompany proxyCompany = new ProxyCompany("蜗牛君");
// 将蜗牛君的身份证号给代理公司,代理公司将个人信息进行挂靠
proxyCompany.entry("1234567890");
// 将社保费用打给代理公司
proxyCompany.paySocialSecurityCost(1700);
}
}
Log日志
<----------------------------
进入个人社保缴费系统
提示:【身份ID:1234567890,姓名:蜗牛君,已进入社保缴费系统!】
---------------------------->
<----------------------------
开始社保缴费流程:
1.缴纳养老保险
2.缴纳失业保险
3.缴纳医疗保险
4.缴纳工伤保险
5.缴纳生育保险
提示:【姓名:蜗牛君,本月社保已缴纳,共支付:1700元】
---------------------------->
4、总结
蜗牛君的社保顺利续费了,又可以安心复习找工作了。关于普通代理模式的特点我们再次强调一下,我们必须知道代理类的存在然后才能访问,反而不必知道具体角色的存在。调用者只能访问代理角色,不能直接访问具体角色,具体角色的产生由代理角色完成。
04
强制代理模式
在蜗牛君失业的同时,蜗牛君的朋友也失业了,更惨的是这位朋友被失业打击的一蹶不振,那就给自己点时间放松放松吧,但是社保还是要缴纳的,于是就来找同样悲催的蜗牛君询问如何缴纳社保。不出意外,蜗牛君把自己的代缴公司介绍给了这位朋友。那么我们的代码要做出哪些改动呢?
1、首先是抽象角色的改动
员工多出了一项技能,就是寻找为自己缴纳社保的公司。
public interface IEmployee {
// 进入个人社保缴费系统
void entry(String idCard);
// 缴纳社保
void paySocialSecurityCost(int cost);
// 查找自己的代理公司
IEmployee getProxyCompany();
}
2、再来看具体角色的改动。
/**
* 员工实现类
*/
public class Employee implements IEmployee {
private String name;
private IEmployee proxyCompany;
public Employee(String name) {
this.name = name;
}
/**
* 登录个人社保缴费账户
* @param idCard
*/
@Override
public void entry(String idCard) {
if (isMyProxyCompany(proxyCompany)) {
// do Something ...
System.out.println("<----------------------------");
System.out.println("进入个人社保缴费系统");
System.out.println("提示:【身份ID:" + idCard + ",姓名:" + name + ",已进入社保缴费系统!】");
System.out.println("---------------------------->");
} else {
throw new Error("这不是我的代理公司,搞错了!!!");
}
}
/**
* 缴纳社保费用
*/
@Override
public void paySocialSecurityCost(int cost) {
if (isMyProxyCompany(proxyCompany)) {
System.out.println("<----------------------------");
System.out.println("开始社保缴费流程:");
System.out.println("1.缴纳养老保险");
System.out.println("2.缴纳失业保险");
System.out.println("3.缴纳医疗保险");
System.out.println("4.缴纳工伤保险");
System.out.println("5.缴纳生育保险");
System.out.println("提示:【姓名:" + name + ",本月社保已缴纳,共支付:" + cost + "元】");
System.out.println("---------------------------->");
} else {
throw new Error("这不是我的代理公司,搞错了!!!");
}
}
/**
* 获取到当前的代理公司
*/
@Override
public IEmployee getProxyCompany() {
proxyCompany = new ProxyCompany(this);
return proxyCompany;
}
/**
* 判断是否是当前的代理公司
* @param proxyCompany
* @return
*/
private boolean isMyProxyCompany(IEmployee proxyCompany) {
if (proxyCompany != null && proxyCompany.getClass().getSimpleName().equals("ProxyCompany")) {
return true;
}
return false;
}
}
大家可以看到具体角色类中多了很关键的一点,就是具体角色内部获取到代理公司的信息,这就是蜗牛君选择的那家代理公司,其他的公司不知道靠不靠谱,只选择这一家代理公司的服务,并且增加了相应的判断。
3、由于抽象角色发生了变动,那么代理角色也要相应的修改,但基本上保持不变。
public class ProxyCompany implements IEmployee {
private IEmployee employee;
/**
* 将自己公司名下的员工实体传递进来
*
* @param employee
*/
public ProxyCompany(IEmployee employee) {
this.employee = employee;
}
/**
* 代理员工登录个人社保缴费系统
*
* @param idCard
*/
@Override
public void entry(String idCard) {
employee.entry(idCard);
}
/**
* 代理员工缴纳社保费用
*
* @param cost
*/
@Override
public void paySocialSecurityCost(int cost) {
employee.paySocialSecurityCost(cost);
}
/**
* 代理的代理暂时还没有,目前返回自己
*
* @return
*/
@Override
public IEmployee getProxyCompany() {
return this;
}
}
4、使用场景的改变
好了,蜗牛君的朋友要通过这家代理公司缴纳社保了,看看具体使用场景是什么样子的吧。
public class Client {
public static void main(String[] args) {
// 创建蜗牛君朋友的身份
IEmployee employee = new Employee("悲催君");
// 查找到我推荐给悲催君的代理公司
IEmployee proxyCompany = employee.getProxyCompany();
// 将悲催君的身份证号给到代理公司
proxyCompany.entry("0987654321");
// 将费用给到代理公司
proxyCompany.paySocialSecurityCost(1700);
}
}
Log日志
<----------------------------
进入个人社保缴费系统
提示:【身份ID:0987654321,姓名:悲催君,已进入社保缴费系统!】
---------------------------->
<----------------------------
开始社保缴费流程:
1.缴纳养老保险
2.缴纳失业保险
3.缴纳医疗保险
4.缴纳工伤保险
5.缴纳生育保险
提示:【姓名:悲催君,本月社保已缴纳,共支付:1700元】
---------------------------->
ok~完事了,蜗牛君的朋友悲催君也顺利的缴纳了社保,可以给自己一些时间好好地放松一下,希望悲催君能够早日从失业的困境中走出来。我们在这里再次强调一下强制代理模式的核心思想:
我们必须知道具体角色的存在,不必知道代理类的存在,代理角色的产生由具体角色完成;从另一个方面说明,真实角色必须指定自己的代理角色。
强制代理模式的特性刚好与普通代理模式相反:普通代理模式是调用者只知道代理角色,而具体角色由代理角色生成;强制代理模式是调用者只知道具体角色,代理角色由具体角色提供。
05
多元化代理模式
其实有件事情蜗牛君忘记和大家说了,就是每次缴纳社保时,代理公司都要收取手续费,毕竟天下没有免费的午餐嘛。而且蜗牛君还发现代理公司还有其他的一些神奇功能。那这些代理公司的属性该如何表述并实现呢?
我们回到最简单的代理模式,抽象角色、具体角色都无需改变,唯一需要改变的是代理角色,我们为代理角色增加一个专属的接口,这个接口里面定义所有代理公司的共性,那就是收费!
1、创建代理公司抽象角色
public interface IProxyCompany {
void count();
}
2、代理角色实现接口
public class ProxyCompany implements IEmployee, IProxyCompany {
private IEmployee employee;
/**
* 构造方法,传入需要代理缴费服务的员工对象
* @param employee
*/
public ProxyCompany(IEmployee employee) {
this.employee = employee;
}
/**
* 将客户信息挂靠到公司下面
* @param idCard
*/
@Override
public void entry(String idCard) {
employee.entry(idCard);
}
/**
* 代理对象支付社保费用
*/
@Override
public void paySocialSecurityCost(int cost) {
employee.paySocialSecurityCost(cost);
count();
}
/**
* 请客户支付代理手续费
*/
@Override
public void count() {
System.out.println("请缴纳手续费100元!");
}
}
3、使用场景展示
public static void main(String[] args) {
Employee employee = new Employee("蜗牛君");
ProxyCompany proxyCompany = new ProxyCompany(employee);
proxyCompany.entry("1234567890");
proxyCompany.paySocialSecurityCost(1700);
}
Log日志
<----------------------------
进入个人社保缴费系统
提示:【身份ID:1234567890,姓名:蜗牛君,已进入社保缴费系统!】
---------------------------->
<----------------------------
开始社保缴费流程:
1.缴纳养老保险
2.缴纳失业保险
3.缴纳医疗保险
4.缴纳工伤保险
5.缴纳生育保险
提示:【姓名:蜗牛君,本月社保已缴纳,共支付:1700元】
---------------------------->
请缴纳手续费100元!
以上代码就是多元化模式的简单展示,这也是代理模式的优点。本身代理角色的存在就是为了增强真实角色,对真实角色进行扩展和过滤。因此代理角色还可以实现其他的接口,加入更多的功能和场景。
06
关于静态代理模式的核心知识点蜗牛君在这里都讲完了。小伙伴们是不是已经体会到代理模式的核心精神以及它的优点了。最后,蜗牛君在这里要划重点了,小伙伴们在找工作的时候一定要擦亮眼睛,给员工缴纳社保是劳动法明文规定的,无论是在试用期还是转正之后。所以大家尽量去那些能够给员工足额缴纳五险一金的公司,有能力去提供七险一金的公司更好。凡是冠冕堂皇的说试用期不给上社保,转正后才有这个福利的公司一律都是黑作坊、骗子公司,大家千万别去。看蜗牛君的文章然后提升实力去大厂才是正解。