装饰者模式(Decorator Pattern)
指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)
属于结构型模式
适用场景:
1.用于扩展一个类的功能或者给一个类添加附加职责。
2.动态的给一个对象添加功能,这些功能可以动态的撤销。
现实场景:
大家经常吃炒粉不是,有原味,有加肉,有加蛋,也有我全都要的,我们马上想到了代码形式。
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 原味炒粉
* @E-Mail : [email protected]
* @Date : Created in 15:55 2020-3-14
*/
public class Noodles {
protected String getMsg(){
return "炒粉";
}
protected int getPrice(){
return 6;
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 15:59 2020-3-14
*/
public class NoddlesWithEggs extends Noodles {
@Override
protected String getMsg() {
return super.getMsg()+",加一个鸡蛋";
}
@Override
protected int getPrice() {
return super.getPrice()+1;
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 16:01 2020-3-14
*/
public class NoodlesWithEggsAndMeat extends NoddlesWithEggs{
@Override
protected String getMsg() {
return super.getMsg()+",加点猪肉";
}
@Override
protected int getPrice() {
return super.getPrice()+2;
}
}
测试下运行:
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 16:02 2020-3-14
*/
public class client {
public static void main(String[] args) {
//来份原味炒粉
Noodles noodles = new Noodles();
System.out.println(noodles.getMsg() + "价格:"+noodles.getPrice());
//来份鸡蛋炒粉
NoddlesWithEggs noddlesWithEggs = new NoddlesWithEggs();
System.out.println(noddlesWithEggs.getMsg() + "价格:"+noddlesWithEggs.getPrice());
//我全都要,来份加肉加蛋的
NoodlesWithEggsAndMeat noodlesWithEggsAndMeat = new NoodlesWithEggsAndMeat();
System.out.println(noodlesWithEggsAndMeat.getMsg() + "价格:"+noodlesWithEggsAndMeat.getPrice());
}
}
但是有的人又会说了,我要加二个鸡蛋怎么办呢,显然,不可能来个类noddlesWithDoubleEgg,对吧,所以我们继续进行优化。,这个时候装饰者模式就登场了。
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 此时炒粉就变成了一个抽象的炒粉了
* @E-Mail : [email protected]
* @Date : Created in 16:13 2020-3-14
*/
public abstract class Noodles {
//信息
protected abstract String getMsg();
//价格
protected abstract int getPrice();
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 炒粉装饰器
* @E-Mail : [email protected]
* @Date : Created in 16:16 2020-3-14
*/
public abstract class NoodlesDecorator extends Noodles{
//又像静态代理
private Noodles noodles;
public NoodlesDecorator(Noodles noodles){
this.noodles = noodles;
}
@Override
protected String getMsg() {
return this.noodles.getMsg();
}
@Override
protected int getPrice() {
return this.noodles.getPrice();
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 基础套餐,原味炒粉
* @E-Mail : [email protected]
* @Date : Created in 16:15 2020-3-14
*/
public class BaseNoodles extends Noodles{
@Override
protected String getMsg() {
return "原味炒粉";
}
@Override
protected int getPrice() {
return 6;
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 加鸡蛋
* @E-Mail : [email protected]
* @Date : Created in 16:21 2020-3-14
*/
public class EggsDecorator extends NoodlesDecorator{
public EggsDecorator(Noodles noodles) {
super(noodles);
}
@Override
protected String getMsg() {
return super.getMsg()+ "+1个鸡蛋";
}
@Override
protected int getPrice() {
return super.getPrice()+1;
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 加肉
* @E-Mail : [email protected]
* @Date : Created in 16:23 2020-3-14
*/
public class MeatDecorator extends NoodlesDecorator{
public MeatDecorator(Noodles noodles) {
super(noodles);
}
@Override
protected String getMsg() {
return super.getMsg() + "+1份肉";
}
@Override
protected int getPrice() {
return super.getPrice()+2;
}
}
测试运行一下:
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 16:25 2020-3-14
*/
public class DecoratorTest {
public static void main(String[] args) {
Noodles noodles;
//路边买了个原味炒粉
noodles = new BaseNoodles();
//不爽啊,来个蛋
noodles = new EggsDecorator(noodles);
//还是不行啊,我全都要,再加份肉
noodles = new MeatDecorator(noodles);
//还是不行,真男人,我要双黄蛋,再来一个蛋
noodles = new EggsDecorator(noodles);
System.out.println(noodles.getMsg() + "价格:"+noodles.getPrice());
}
这就是装饰者模式的功能了,想加几个蛋就加几个,你还可以继续加香肠也没问题,不需要再去改变原有的代码。
好,上面那个是现实中的例子,现在我们来举个代码中用到的例子,第三方登陆形式二。
初始登陆方式:
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 登陆接口
* @E-Mail : [email protected]
* @Date : Created in 16:44 2020-3-14
*/
public interface LoginService {
public MsgResult login(String username, String password);
public MsgResult register(String username,String password);
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 相当于系统已经写好的代码,不去动它
* @E-Mail : [email protected]
* @Date : Created in 13:38 2020-3-14
*/
public class LoginServiceImpl implements LoginService{
//用户名,密码登陆方法
@Override
public MsgResult login(String username, String password){
System.out.println("用户pc登陆");
return null;
}
//注册方法
@Override
public MsgResult register(String username,String password){
return login(username,password);
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:07 2020-3-12
*/
@Data
@AllArgsConstructor
public class MsgResult {
private int code;
private Object data;
private String msg;
public static MsgResult success(Object data){
return new MsgResult(200,data,"成功");
}
public static MsgResult fail(Object data){
return new MsgResult(404,data,"失败");
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 10:57 2020-3-13
*/
@Data
public class User {
private String id;
private String usename;
private String password;
private int age;
private String addr;
}
配上第三方登陆,使用装饰器加强来实现,不去动原有的代码:
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 第三方登陆接口,直接继承登陆接口
* @E-Mail : [email protected]
* @Date : Created in 16:48 2020-3-14
*/
public abstract class ThirdLoginServiceDecorator implements LoginService {
//qq登陆留给子类实现
protected MsgResult loginQQ(String id){
return null;
}
//微信登陆留给子类实现
protected MsgResult loginWechat(String id){
return null;
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 实现原有的基础套餐
* @E-Mail : [email protected]
* @Date : Created in 16:50 2020-3-14
*/
public class ThirdLoginServiceImpl extends ThirdLoginServiceDecorator {
private LoginService loginService;
public ThirdLoginServiceImpl(LoginService loginService){
this.loginService = loginService;
}
@Override
public MsgResult login(String username, String password) {
return loginService.login(username,password);
}
@Override
public MsgResult register(String username, String password) {
return loginService.register(username,password);
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption qq登陆装饰器
* @E-Mail : [email protected]
* @Date : Created in 16:57 2020-3-14
*/
public class ThirdLoginQQDecorator extends ThirdLoginServiceDecorator {
private ThirdLoginServiceDecorator thirdLoginServiceDecorator;
public ThirdLoginQQDecorator(ThirdLoginServiceDecorator thirdLoginServiceDecorator){
this.thirdLoginServiceDecorator = thirdLoginServiceDecorator;
}
@Override
public MsgResult loginQQ(String id){
System.out.println("qq登陆");
return null;
}
@Override
public MsgResult login(String username, String password) {
return thirdLoginServiceDecorator.login(username,password);
}
@Override
public MsgResult register(String username, String password) {
return thirdLoginServiceDecorator.register(username,password);
}
}
测试一下:
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 17:00 2020-3-14
*/
public class updateLoginTest {
public static void main(String[] args) {
ThirdLoginServiceDecorator thirdLoginService = new ThirdLoginServiceImpl(new LoginServiceImpl());
thirdLoginService = new ThirdLoginQQDecorator(thirdLoginService);
thirdLoginService.loginQQ("4444");
thirdLoginService.login("xiaoming","123456");
}
}
感觉好像适配器模式是不是啊,其实代码上看起来像,但是重要的是两种模式的思想,我们这里来做一下区别。
我们来看看jdk中运用最经典的装饰者模式,io,发现了没有io流中就是new一级一级来传递的(io里面有个很经典的总结,大桶套小桶)。
把上面的抽象类注入子类,拿到一些父类的东西,完全符合大桶套小桶。
总结:
优点:
1.装饰者是继承的有利的补充,比继承灵活,不改变原有对象的情况下动态的给一个对象扩展功能,即插即用。
2.通过使用不同装饰类以及这些装饰类的排列组合可以实现不同的效果。
3.装饰者完全遵守开闭原则。
缺点:
1.增加更多的类,增加程序的复杂性。
2.动态装饰时,多层装饰时会更复杂。