Spring 5 设计模式 - Structural

structural模式被用来定义对象(继承或者组合)之间的关系。Structural模式确保部分的改变不会导致整体也跟着改变。

adapter

适配器模式让两个不相容的类(因为接口不相容)一起工作。它像桥一样工作于两个不相容的接口之间。

带来的好处:

  • 让不相容的类一起工作
  • 提高旧代码的可重用性

Spring使用的适配器模式

Spring内部大量使用了适配器模式,比如JpaVendorAdapter、HibernateJpaVendorAdapter、HandlerInterceptorAdapter、SpringContextResourceAdapter等。

简单的实现

Spring 5 设计模式 - Structural

先看旧的支付网关:

public interface PaymentGateway {
    void doPayment(Account account1, Account account2);
}

public class PaymentGatewayImpl implements PaymentGateway{
    @Override
    public void doPayment(Account account1, Account account2){
        System.out.println("Do payment using Payment Gateway");
    }
}

下面是新网关:

public interface AdvancedPayGateway {
    void makePayment(String mobile1, String mobile2);
}

public class AdvancedPaymentGatewayAdapter implements AdvancedPayGateway {
    private PaymentGateway paymentGateway;
    public AdvancedPaymentGatewayAdapter(PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }

    public void makePayment(String mobile1, String mobile2) {
        Account account1 = null;//get account number by mobile 
        Account account2 = null;//get account number by mobile 
        paymentGateway.doPayment(account1, account2);
    }
}

下来是测试类:

public class AdapterPatternMain {
    public static void main(String[] args) {
        PaymentGateway paymentGateway = new PaymentGatewayImpl();
        AdvancedPayGateway advancedPayGateway = new AdvancedPaymentGatewayAdapter(paymentGateway);
        String mobile1 = null;
        String mobile2 = null;
        advancedPayGateway.makePayment(mobile1, mobile2);
    }
}

这样,我们通过适配器,使用新的方法,访问了旧代码。

Bridge

桥模式用来解耦客户代码和它的实现。就是说,把抽象和实现分开(继承关系也分开)。Bridge模式优先考虑组合,而不是继承。这样,接口B的实现类或者接口B的修改,不影响抽象类A。桥模式组合一个抽象类A和一个接口B,使用一个接口C作为继承抽象类A的具体类和实现接口B的类之间的一座桥。

带来的好处

  • 允许你把实现和抽象分开
  • 提供修改两端而不影响使用的客户的灵活性
  • 允许向客户隐藏实现细节

Spring中的应用

  • ViewRendererServlet,主要用于Portlet MVC
  • 日志处理

简单的实现

Spring 5 设计模式 - Structural

不使用桥模式,会有很深的继承关系。
Spring 5 设计模式 - Structural

使用桥模式,在Bank接口和Account接口之间增加了一个关系。
Spring 5 设计模式 - Structural

我们先看帐号:

public interface Account {
    Account openAccount();
    void accountType();
}

public class SavingAccount implements Account {
    @Override
    public Account openAccount() {
        System.out.println("OPENED: SAVING ACCOUNT ");
        return new SavingAccount();
    }
    @Override
    public void accountType() {
        System.out.println("##It is a SAVING Account##");
    }
}

public class CurrentAccount implements Account {
    @Override
    public Account openAccount() {
        System.out.println("OPENED: CURRENT ACCOUNT ");
        return new CurrentAccount();
    }
    @Override
    public void accountType() {
        System.out.println("##It is a CURRENT Account##");
    }
}

再看银行:

public abstract class Bank {
    //Composition with implementor
    protected Account account;
    public Bank(Account account){
        this.account = account;
    }
    abstract Account openAccount();
}

public class IciciBank extends Bank {
    public IciciBank(Account account) {
        super(account);
    }
    @Override
    Account openAccount() {
        System.out.print("Open your account with ICICI Bank");
        return account;
    }
}

public class HdfcBank extends Bank {
    public HdfcBank(Account account) {
        super(account);
    }
    @Override
    Account openAccount() {
        System.out.print("Open your account with HDFC Bank");
        return account;
    }
}

我们的测试代码

public class BridgePatternMain {
    
    public static void main(String[] args) {
        Bank icici = new IciciBank(new CurrentAccount());
        Account current = icici.openAccount();
        current.accountType();
        Bank hdfc = new HdfcBank(new SavingAccount());
        Account saving = hdfc.openAccount();
        saving.accountType();
    }
}

Composite

相同类型的一组对象被客户当作一个对象看待,这些对象组合成一个树结构。

简单的实现

Spring 5 设计模式 - Structural

先看帐号:

public interface Account {
    void accountType();
}

public class SavingAccount implements Account {
    @Override
    public void accountType() {
        System.out.println("SAVING ACCOUNT");
    }
}

public class CurrentAccount implements Account {
    @Override
    public void accountType() {
        System.out.println("CURRENT ACCOUNT");
    }
}

CompositeBankAccount被当作Composite,实现了Account接口:

public class CompositeBankAccount implements Account {
    //Collection of child accounts.
    private List<Account> childAccounts = new ArrayList<Account>();
    @Override
    public void accountType() {
        for (Account account : childAccounts) {
            account.accountType();
        }
    }
    //Adds the account to the composition.
    public void add(Account account) {
        childAccounts.add(account);
    }
    //Removes the account from the composition.
    public void remove(Account account) {
        childAccounts.remove(account);
    }
}

测试代码

public class CompositePatternMain {
    
    public static void main(String[] args) {
        SavingAccount savingAccount1 = new SavingAccount();
        SavingAccount savingAccount2 = new SavingAccount();

        CurrentAccount currentAccount1 = new CurrentAccount();
        CurrentAccount currentAccount2 = new CurrentAccount();

        CompositeBankAccount compositeBankAccount1 = new CompositeBankAccount();
        CompositeBankAccount compositeBankAccount2 = new CompositeBankAccount();
        CompositeBankAccount compositeBankAccount = new CompositeBankAccount();

        compositeBankAccount1.add(savingAccount1);
        compositeBankAccount1.add(currentAccount1);
        compositeBankAccount2.add(currentAccount2);
        compositeBankAccount2.add(savingAccount2);
        compositeBankAccount.add(compositeBankAccount2);
        compositeBankAccount.add(compositeBankAccount1);
        compositeBankAccount.accountType();
    }
}

Decorator

装饰模式允许你在运行时动态或者静态地增加或者减少一个对象的行为。装饰模式通过继承组合对象,允许你把某一领域的功能分到不同的具体实现类。
装饰模式也叫Wrapper。

比如银行把顾客分成三类:senior citizens、privileged和young。该银行启动了老年人储蓄账户计划,如果开一个储蓄账户,可以获得最多1000元的医疗保险。对于privileged顾客,额度是1600元,还可以透支84元。young顾客没有任何计划。
我们增加一个子类SavingAccount,类结构图可能是这样的:
Spring 5 设计模式 - Structural

如果使用装饰模式,可以这样设计:
Spring 5 设计模式 - Structural

这样,AccountDecorator和Account既是Is-a的关系(继承),也是Has-a的关系(不改变旧代码,通过组合增加新功能)。

UML图如下
Spring 5 设计模式 - Structural

简单的实现

public interface Account {
    String getTotalBenefits();
}

public class SavingAccount implements Account {
    @Override
    public String getTotalBenefits() {
        return "This account has 4% interest rate with per day $5000 withdrawal limit";
    }
}

public class CurrentAccount implements Account {
    @Override
    public String getTotalBenefits() {
        return "There is no withdrawal limit for current account";
    }
}

下来是装饰类

public abstract class AccountDecorator implements Account {
    abstract String applyOtherBenefits();
}

public class SeniorCitizen extends AccountDecorator {
    Account account;
    public SeniorCitizen(Account account) {
        super();
        this.account = account;
    }
    public String getTotalBenefits() {
        return account.getTotalBenefits() + " other benefits are " + applyOtherBenefits();
    }
    String applyOtherBenefits() {
        return " an medical insurance of up to $1,000 for Senior Citizen";
    }
}

public class Privilege extends AccountDecorator {
    Account account;
    public Privilege(Account account) {
        this.account = account;
    }
    public String getTotalBenefits() {
        return account.getTotalBenefits() + " other benefits are " + applyOtherBenefits();
    }
    String applyOtherBenefits() {
        return " an accident insurance of up to $1,600 and an overdraft facility of $84";
    }
}

测试代码

public class DecoratorPatternMain {
    public static void main(String[] args) {
        Account basicSavingAccount = new SavingAccount();
        System.out.println(basicSavingAccount.getTotalBenefits());

        Account seniorCitizenSavingAccount = new SavingAccount();
        seniorCitizenSavingAccount = new SeniorCitizen(seniorCitizenSavingAccount);
        System.out.println(seniorCitizenSavingAccount.getTotalBenefits());

        Account privilegeCitizenSavingAccount = new SavingAccount();
        privilegeCitizenSavingAccount = new Privilege(privilegeCitizenSavingAccount);
        System.out.println(privilegeCitizenSavingAccount.getTotalBenefits());
    }
}

Spring中的应用

  • 事务、缓存同步、安全等功能都使用了装饰模式
  • AOP功能也通过CGLib使用了装饰模式
  • BeanDefinitionDecorator:通过自定义属性装饰bean定义
  • WebSocketHandlerDecorator:给WebSocketHandler增加附加行为

Facade

Facade模式使用接口简化客户和子系统之间的交互。

Spring 5 设计模式 - Structural

使用Facade模式以后:
Spring 5 设计模式 - Structural

简单的实现

public class PaymentService {
    public static boolean doPayment() {
        return true;
    }
}

public class AccountService {
    public static Account getAccount(String accountId) {
        return new SavingAccount();
    }
}

public class TransferService {
    public static void transfer(int amount, Account fromAccount, Account toAccount) {
        System.out.println("Transfering Money");
    }
}

增加Facade类

public interface BankingServiceFacade {
    void moneyTransfer();
}

public class BankingServiceFacadeImpl implements BankingServiceFacade {
    @Override
    public void moneyTransfer() {
        if(PaymentService.doPayment()) {
            Account fromAccount = AccountService.getAccount("1");
            Account toAccount = AccountService.getAccount("2");
            TransferService.transfer(1000, fromAccount, toAccount);
        }
    }
}

增加Facade的Client:

public class FacadePatternClient {
    public static void main(String[] args) {
        BankingServiceFacade serviceFacade = new BankingServiceFacadeImpl();
        serviceFacade.moneyTransfer();
    }
}

Proxy

Proxy模式提供一个对象,拥有另一个类的功能。这样可以向外部隐藏实际的对象。

Spring 5 设计模式 - Structural

简单的实现

public interface Account {
    void accountType();
}

public class SavingAccount implements Account {
    public void accountType() {
        System.out.println("SAVING ACCOUNT");
    }
}

代理类

public class ProxySavingAccount implements Account {
    private Account savingAccount;
    public void accountType() {
        if(savingAccount == null){
            savingAccount = new SavingAccount();
        }
        savingAccount.accountType();
    }
}

Spring中的应用

Spring AOP使用了代理模式。另外,HTTP Invoker等很多功能也使用了代理模式。