目录
外观模式
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。
类型:结构型
使用场景:当子系统越来越复杂的时候,增加外观模式可以提供简单调用接口。构建多层系统结构的时候,利用外观对象作为每层的入口,简化层间调用。
优点:简化调用过程,无需深入了解子系统,防止带来风险。减少系统以来,松散耦合。更好的划分访问层次。符合迪米特法则。
缺点:增加、扩展子系统的行为容易引入风险。不符合开闭原则。
例子:
public class Goods {
private String name;
public Goods(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class OrderService {
public boolean isAvailable(Goods goods){
System.out.println("商品:" + goods.getName() + ",库存校验通过,下单成功");
return true;
}
}
public class PaymentService {
public boolean pay(Goods goods){
System.out.println("支付" + goods.getName() + "成功");
return true;
}
}
public class ShippingService {
public String shipGoods(Goods goods){
System.out.println(goods + "进入物流系统");
String orderNum = "101";
return orderNum;
}
}
public class StoreService {
private OrderService orderService;
private PaymentService paymentService;
private ShippingService shippingService;
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void setShippingService(ShippingService shippingService) {
this.shippingService = shippingService;
}
public void goodsSell(Goods goods){
if(orderService.isAvailable(goods)){
if(paymentService.pay(goods)){
String shippingNum = shippingService.shipGoods(goods);
System.out.println("下单成功,订单号:" + shippingNum);
}
}
}
}
public class Test {
public static void main(String[] args) {
Goods goods = new Goods("衣服");
StoreService storeService = new StoreService();
storeService.setOrderService(new OrderService());
storeService.setPaymentService(new PaymentService());
storeService.setShippingService(new ShippingService());
storeService.goodsSell(goods);
}
}
看下类图
事实上这种方式并不符合外观模式,对于外观模式,应用层只需要和StoreService关联就好,不需要跟其他Service关联,而这里在应用层也创建了其他service,所以需要调整一下代码
public class StoreService {
private OrderService orderService = new OrderService();
private PaymentService paymentService = new PaymentService();
private ShippingService shippingService = new ShippingService();
public void goodsSell(Goods goods){
if(orderService.isAvailable(goods)){
if(paymentService.pay(goods)){
String shippingNum = shippingService.shipGoods(goods);
System.out.println("下单成功,订单号:" + shippingNum);
}
}
}
}
public class Test {
public static void main(String[] args) {
Goods goods = new Goods("衣服");
StoreService storeService = new StoreService();
storeService.goodsSell(goods);
}
}
再看类图
这样才符合外观模式。
用到设计模式的源码:
Configuration#newMetaObject主要看其被调用的方法,调用方法想要一个MetaObject,只需要调Configuration这个方法就好,而不需要直接和MetaObject有关系。
装饰者模式
在不改变原油对象的基础上,将功能附加到对象上。提供了比继承更有弹性的替代方案(扩展原有对象功能)。
我们通常使用继承进行扩展,但如果扩展的功能种类繁多,那就会生成很多子类来增加系统的复杂性。并且使用继承扩展,必须可预见这些功能。
类型:结构型
使用场景:扩展一个类的功能或给一个类添加附加职责。动态给一个兑现更添加功能,这些功能可以再动态撤销。
优点:是继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能。通过使用不同的装饰类以及这些装饰类的排列组合,可以实现不同的效果。符合开闭原则。
缺点:会出现更多代码,更多的对象,增加系统复杂性。动态或多层装饰会更复杂。
例子:
public class Cake {
protected String getName(){
return "蛋糕";
}
protected int price(){
return 10;
}
}
public class MangoCake extends Cake{
@Override
public String getName() {
return "芒果" + super.getName();
}
@Override
public int price() {
return super.price() + 5;
}
}
public class MangoAppleCake extends MangoCake {
@Override
public String getName() {
return "苹果" + super.getName();
}
@Override
public int price() {
return super.price() + 6;
}
}
public class Test {
public static void main(String[] args) {
Cake cake = new Cake();
System.out.println(cake.getName() + ",价格:" + cake.price());
cake = new MangoCake();
System.out.println(cake.getName() + ",价格:" + cake.price());
cake = new MangoAppleCake();
System.out.println(cake.getName() + ",价格:" + cake.price());
}
}
这种写法对于扩展来说非常困难,蛋糕有各种各样的,如果我还想要别的种类,那还要加更多的类,更麻烦,于是用装饰者模式试试:
public abstract class AbstractCake {
protected abstract String getName();
protected abstract int price();
}
public class AbstractDecorator extends AbstractCake {
private AbstractCake aCake;
public AbstractDecorator(AbstractCake aCake) {
this.aCake = aCake;
}
@Override
protected String getName() {
return this.aCake.getName();
}
@Override
protected int price() {
return this.aCake.price();
}
}
public class Cake extends AbstractCake {
protected String getName(){
return "蛋糕";
}
protected int price(){
return 10;
}
}
public class Apple extends AbstractDecorator {
public Apple(AbstractCake aCake) {
super(aCake);
}
@Override
protected String getName() {
return "苹果" + super.getName();
}
@Override
protected int price() {
return super.price() + 6;
}
}
public class Mango extends AbstractDecorator{
public Mango(AbstractCake aCake) {
super(aCake);
}
@Override
public String getName() {
return "芒果" + super.getName();
}
@Override
public int price() {
return super.price() + 5;
}
}
public class Test {
public static void main(String[] args) {
AbstractCake cake = new Cake();
cake = new Mango(cake);
cake = new Mango(cake);
cake = new Apple(cake);
System.out.println(cake.getName() + ",价格:" + cake.price());
}
}
再看下类图:
用这种方式,可以很自由的选择加什么东西,扩展起来也更方便。
用到设计模式的源码:
BufferedReader继承Reader,并且用in来构建BufferedReader
ServletRequest
直接看类图:
mybatis中的Cache
上面decorators中的类都是cache的装饰类,可以自己去看看。
适配器模式
将一个类的接口(被适配者)转换成客户期望的另一个接口(目标)。使原本不兼容的类可以一起工作。
类型:结构型
使用场景:已经存在的类的方法和需求不匹配时(方法和结果相同或相似)。不是软件设计阶段考虑的设计模式,而是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同的情况下的解决方案。
优点:能提高类的透明性和复用性,现有的类复用但不需要改变。目标类和适配器类解耦,提高程序的扩展性。符合开闭原则。
缺点:在编写过程中要全面考虑,可能会在增加系统的复杂性。增加系统代码的可读难度。
例子:
类适配器模式:通过集成获取被适配者的方法,并修改为自己的实现。
public class Adaptee {
public void adapteeRequest(){
System.out.println("被适配者的方法");
}
}
public interface Target {
void request();
}
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("ConcreteTarget目标方法");
}
}
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
super.adapteeRequest();
}
}
public class Test {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
类图:
目标方法通过Target的接口,Target通过Adapter移交给了被适配者(Adaptee)
对象适配器模式
一个场景:电源适配器,从220v交流电转换为手机可用的5v直流电
public class AC220 {
public int outputAC220V(){
int output = 220;
System.out.println("输入交流电" + output + "V");
return output;
}
}
public interface DC5 {
int outputDC5V();
}
public class PowerAdapter implements DC5 {
private AC220 ac220 = new AC220();
@Override
public int outputDC5V() {
int adapterInput = ac220.outputAC220V();
int adapterOutput = adapterInput / 44;
System.out.println("使用适配器,输入" + adapterInput + ",输出" + adapterOutput);
return adapterOutput;
}
}
public class Test {
public static void main(String[] args) {
DC5 dc5 = new PowerAdapter();
dc5.outputDC5V();
}
}
类图:
用到设计模式的源码:
例如XmlAdapter
xml读取的一个抽象类,用的时候继承这个类并实现相关接口
其实适配器模式在spring启动配置时经常用到,我们在之前springboot的源码分析中也可以见到大量的Adapter,DispatcherServlet#doDispatch中
因为会传入若干handler,那也有若干适配器与他匹配,适配器实现HandlerAdapter接口。