(一)面向复用性的Java设计模式

目录

        • 目录
        • 0、写在前面
        • 1、面向可复用性的设计模式
          • 1.1、结构型模式
            • 1.1.1、Adapter(适配器模式)
            • 1.1.2、Decorator(装饰器模式)
            • 1.1.3、Facade(外观模式模式)
          • 1.2、行为类模式
            • 1.2.1、Strategy(策略模式)
            • 1.2.2、Template(模板模式)
            • 1.2.3、Iterator(迭代器模式)

0、写在前面

万变不离其宗,无外乎委托+继承。
设计模式的分类:
①创建型模式(关注对象的创建过程)
factory(工厂模式)
abstract factory(抽象工厂模式)
builder(建造者模式)
(面向性能的模式,尽量少创建对象)
singleton(单例模式)
prototype(原型模式)
flyweight(轻量模式)
object pool(对象池)

②结构型模式(处理类和对象的构成)
(使用各种继承和组合以及委托)
Adapter(适配器模式)
Decorator(装饰器模式)
Facade(外观模式模式)
bridge(桥接模式)
proxy(代理模式)
composite(组合模式)

③行为类模式(表征类或对象交互和分配责任的方式)
Strategy(策略模式)
Template(模板模式)
Iterator(迭代模式)
observer(观察者模式)
visitor(访问者模式)
state(状态模式)
memento(备忘录模式)

1、面向可复用性的设计模式

1.1、结构型模式
1.1.1、Adapter(适配器模式)

将某个类/接口转换为client期望的其他形式。
继承树
(一)面向复用性的Java设计模式_第1张图片
举例
(一)面向复用性的Java设计模式_第2张图片
Rectangle作为一个适配器,内部使用委托调用LegacyRectangle类的方法,实现将LegacyRectangle中的display转化成client**期望的方式,体现为参数命名**上。在这里对抽象接口进行编程,所以定义了一个Shape接口,Rectangle实现Shape。
在后续的所有模式中,都不是直接创建一个类,而是先创建一个接口,然后类实现接口,并且使用SuperInterface obj = new SubClass()的形式创建对象,其实这就是接口转置原则(SOLID中的DIP)。

1.1.2、Decorator(装饰器模式)

为对象增加不同侧面的特性,对每一个特性构造子类,通过委派机制增加到对象上。
继承树
(一)面向复用性的Java设计模式_第3张图片
举例

/* 父接口 */
interface Stack {
    void push(Item e);
    Item pop();
}
/* 抽象子类 */
public abstract class AbstractStackDecorator implements Stack {
    /* 委托 */
    protected final Stack stack;
    public AbstractStackDecorator(Stack stack) {
        this.stack = stack;
    }
    public void push(Item e) {
        stack.push(e);
    }
    public Item pop() {
        return stack.pop();
    }
    ...
}
/* 实际装饰器类,子类型 */
public class UndoStack extends AbstractStackDecorator implements Stack {
    private final UndoLog log = new UndoLog();
    public UndoStack(Stack stack) {
        super(stack);
    }
    public void push(Item e) {
        log.append(UndoLog.PUSH, e);
        super.push(e);
    }
    public void undo() {
    //implement decorator behaviors on stack
    }
...
}
/* 创建一个普通堆栈,ArrayStack实现了Stack接口 */
    Stack s = new ArrayStack();
/* 创建一个撤销栈,只有一个撤销的装饰器 */
    UndoStack s = new UndoStack(new ArrayStack());
/* 创建一个线程安全的撤销栈,有两个装饰器  */
    SecureStack s = new SecureStack(new SynchronizedStack(new UndoStack(s))

本来存在的是Stack接口,然后AbstractStack使用委托实现了Stack的所有操作(其实也可以在装饰器中实现,但是为了使装饰器只实现一个功能,同时也为了实现更多的装饰器,所以特意创建一个抽象子类);最后创建装饰器类,继承了AbstractStack,实现了Stack接口,内部实现具体的装饰,比如记录入栈日志(原本的入栈操作使用super调用父类中的实现),以及撤销操作。
过程本质可以从创建对象的方式上看出,传入一个对象,进行装饰,然后返回一个新的对象。
Java集合类中常见的装饰器有:

/* Turn a mutable list into an immutable list */
    static List<T> unmodifiableList(List<T> lst);
    static Set<T> unmodifiableSet( Set<T> set);
    static Map<K,V> unmodifiableMap( Map<K,V> map);
/* Similar for synchronization */
    static List<T> synchronizedList(List<T> lst);
    static Set<T> synchronizedSet( Set<T> set);
    static Map<K,V> synchronizedMap( Map<K,V> map);
1.1.3、Facade(外观模式模式)

客户端需要通过一个简化的接口来访问复杂系统内的功能;提供一个统一的接口来取代一系列小接口调用,相当于对复杂系统做了一个封装简化客户端使用。(解耦
形式化(上层客户端只需要调用一个接口,并传入选择代号,内部进行选择操作,最后返回需求)
(一)面向复用性的Java设计模式_第4张图片
举例

/* 两个类分别封装客户端所需的功能 */
public class MySqlHelper {
    public static Connection getMySqlDBConnection() {…}
    public void generateMySqlPDFReport(String tableName, Connection con){…}
    public void generateMySqlHTMLReport(String tableName, Connection con){…}
}
public class OracleHelper {
    public static Connection getOracleDBConnection() {…}
    public void generateOraclePDFReport(String tableName, Connection con){…}
    public void generateOracleHTMLReport(String tableName, Connection con){…}
}
/* 外观类 */
public class HelperFacade {
    public static void generateReport(DBTypes dbType, ReportTypes reportType, String tableName){
    Connection con = null;
    switch (dbType){
        case MYSQL:
            con = MySqlHelper.getMySqlDBConnection();
            MySqlHelper mySqlHelper = new MySqlHelper();
            switch(reportType){
                case HTML:
                    mySqlHelper.generateMySqlHTMLReport(tableName, con);
                    break;
                case PDF:
                    mySqlHelper.generateMySqlPDFReport(tableName, con);
                    break;
            }
            break;
    case ORACLE: …
}
/* 选择 */
    public static enum DBTypes { MYSQL,ORACLE; }
    public static enum ReportTypes { HTML,PDF;}
}
/* 客户端调用 */
/* 选择:MYSQL,HTML */
HelperFacade.generateReport(HelperFacade.DBTypes.MYSQL,HelperFacade.ReportTypes.HTML, tableName);
/* 选择:ORACLE,PDF */
HelperFacade.generateReport(HelperFacade.DBTypes.ORACLE,HelperFacade.ReportTypes.PDF, tableName);

首先,有两个类OracleHelperMySQLHelper,分别封装了客户端的需求(产生HTMl类型、PDF类型报告);然后使用外观类HelperFacede,使用枚举,给出了数据库类型的选择和报告类型的选择,在generateReport中,使用switch-case语句,根据客户端传入的数据库类型进行选择,每个数据库类型中,再根据操作类型进行选择,最后实现需求。
外观模式,主要是通过创建一个外观辅助类,简化了客户端的调用,客户端只需要传入选择即可,在外观辅助类中通过if-elseswitch-case语句,根据客户端的选择分情况进行操作。
需要注意的是,外观辅助类中使用了静态方法枚举变量,这也是一种简化,便于客户端直接调用。

1.2、行为类模式
1.2.1、Strategy(策略模式)

问题:针对特定任务存在不同的算法,但客户端可以根据动态上下文在运行时切换算法。
示例:对客户列表进行排序(冒泡排序,合并排序,快速排序)。
解决方案:为算法创建一个接口,并为算法的每个变体创建一个实现类。
优点: 易于扩展新算法实现,从客户端上下文中分离算法。
继承树(反而一头雾水,看代码分析)
(一)面向复用性的Java设计模式_第5张图片
举例

/* 支付策略接口 */
public interface PaymentStrategy {
    public void pay(int amount);
}
/* 信用卡支付,策略1 */
public class CreditCardStrategy implements PaymentStrategy {
    ...
    public CreditCardStrategy(String nm, String ccNum,String cvv, String expiryDate){
    ...
    }
    @Override
    public void pay(int amount) {
        System.out.println(amount +" paid with credit card");
    }
}
/* Paypal支付,策略2 */
public class PaypalStrategy implements PaymentStrategy {
    ...
    public PaypalStrategy(String email, String pwd){
    ...
    }
    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using Paypal.");
    }
}
/* 购物清单-管理支付策略类 */
public class ShoppingCart {
    /* 有addItem()添加物品操作;calculateTotal()计算总价操作 */
    ...
    public void pay(PaymentStrategy paymentMethod){
        /*  */
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}
/* 客户端 */
public class ShoppingCartTest {
    public static void main(String[] args) {
        /* 创建策略管理类 */
        ShoppingCart cart = new ShoppingCart();
        /* 添加物品 */
        Item item1 = new Item("1234",10);
        Item item2 = new Item("5678",40);
        cart.addItem(item1);
        cart.addItem(item2);
        /* 使用不同的支付方式 */
        //pay by paypal
        cart.pay(new PaypalStrategy("[email protected]", "mypwd"));
        //pay by credit card
        cart.pay(new CreditCardStrategy(“Alice", "1234", "786", "12/18"));
    }
}

策略模式,主要分为策略接口(支付策略接口),多个实现接口的策略子类(信用卡支付,PayPal支付),策略管理类(ShoppingCart购物清单类)。最重要的是选择策略方式,

public void pay(PaymentStrategy paymentMethod){
    int amount = calculateTotal();
    paymentMethod.pay(amount);
}

其实策略的选择创建在客户端中实现,客户端传入具体策略对象,然后在策略管理类中进行下一步操作。
可以发现,只有在客户端选择策略时是直接创建策略子类,其他地方一律使用的策略接口。

1.2.2、Template(模板模式)

做事情的步骤一样,但具体方法不同,共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现。使用继承和重写实现模板模式。
继承树
(一)面向复用性的Java设计模式_第6张图片
举例

/* 模板:父类,抽象类,包含统一的行为-抽象的行为和具体的行为 */
public abstract class OrderProcessTemplate {
    public boolean isGift;
    /* 抽象方法,子类中具体实现 */
    public abstract void doSelect();
    public abstract void doPayment();
    public abstract void doDelivery();
    /* 共有方法 */
    public final void giftWrap() {
        System.out.println("Gift wrap done.");
    }
    public final void processOrder() {
        doSelect();
        doPayment();
        if (isGift)
            giftWrap();
        doDelivery();
    }
}
/* 模板的一种实现 */
public class NetOrder extends OrderProcessTemplate {
    @Override
    public void doSelect() { … }
    @Override
    public void doPayment() { … }
    @Override
    public void doDelivery() { … }
}
/* 模板的另一种实现 */
public class StoreOrderer extends OrderProcessTemplate {
    @Override
    public void doSelect() { … }
    @Override
    public void doPayment() { … }
    @Override
    public void doDelivery() { … }
}
/* 客户端 */
    OrderProcessTemplate netOrder = new NetOrder();
    netOrder.processOrder();
    OrderProcessTemplate storeOrder = new StoreOrder();
    storeOrder.processOrder();

总体上和策略模式很像,但是策略模式中策略接口只有一种方法,而模板模式中,模板是一个抽象类,有具体方法,有抽象方法,而实现模板就是实现抽象的方法。
可以发现,在客户端使用时,仍然是面向接口编程。

1.2.3、Iterator(迭代器模式)

Iterable接口:实现该接口的集合对象是可迭代遍历的:

public interface Iterable<T> {
    ...
    Iterator<T> iterator();
}

Iterator接口:迭代器,具有三个方法:

public interface Iterator {
    boolean hasNext();
    E next();
    void remove();
}

让自己的集合类实现Iterable 接口,并实现自己的独特Iterator(hasNext, next, remove),允许客户端利用这个迭代器进行显式或隐式的迭代遍历:

    /* for-each遍历 */
    for (E e : collection) { … }
    /* 使用迭代器对象 */
    Iterator iter = collection.iterator();
    while(iter.hasNext()) { … }

继承树
(一)面向复用性的Java设计模式_第7张图片
举例

/* 实现Iterable接口 */
public class Pair<E> implements Iterable<E> {
    private final E first, second;
    public Pair(E f, E s) {
        first = f; 
        second = s; 
    }
    public Iterator iterator() {
        return new PairIterator();
    }
    /* 实现iterator接口,包含三个方法 */
    private class PairIterator implements Iterator<E> {
        private boolean seenFirst = false, seenSecond = false;
        public boolean hasNext() {
            return !seenSecond; 
        }
        public E next() {
            if (!seenFirst) {
                 seenFirst = true; 
                  return first;
            }
            if (!seenSecond) { 
                seenSecond = true;
                return second; 
            }
            throw new NoSuchElementException();
        }
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
/* 客户端 */
    Pair pair = new Pair("foo", "bar");
    for (String s : pair) { … }

这个模式主要是实现自己的集合类,使其具有使用迭代器的功能。
实现过程是:自己的集合类实现Iterable接口,实现iterator方法,该方法要返回一个Iterator对象,该对象可以通过实现Iterator接口(实现其中的hasNext()next()remove()方法。)来定制。
需要注意的是,过程中统一使用泛型,可以方便任意类型参数使用。
关于迭代器,有一个易错点,就是,使用迭代器遍历时,无法正常使用集合本身的删除操作,此时需要使用iteratorremove操作(在讲内存快照时具体分析过)。

你可能感兴趣的:(SC)