設(shè)計(jì)模式

一显拜、設(shè)計(jì)模式的原則

1.單一職責(zé)原則

  • 一個(gè)類只負(fù)責(zé)一個(gè)功能領(lǐng)域中的相應(yīng)職責(zé)衡奥,或者可以定義為:就一個(gè)類而言爹袁,應(yīng)該只有一個(gè)引起它變化的原因远荠。
  • 單一職責(zé)原則是實(shí)現(xiàn)高內(nèi)聚、低耦合的指導(dǎo)方針失息,它是最簡(jiǎn)單但又最難運(yùn)用的原則譬淳,需要設(shè)計(jì)人員發(fā)現(xiàn)類的不同職責(zé)并將其分離,而發(fā)現(xiàn)類的多重職責(zé)需要設(shè)計(jì)人員具有較強(qiáng)的分析設(shè)計(jì)能力和相關(guān)實(shí)踐經(jīng)驗(yàn)盹兢。

2.開閉原則

  • 一個(gè)軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開放邻梆,對(duì)修改關(guān)閉。即軟件實(shí)體應(yīng)盡量在不修改原有代碼的情況下進(jìn)行擴(kuò)展绎秒。
  • 抽象化是開閉原則的關(guān)鍵浦妄。可以為系統(tǒng)定義一個(gè)相對(duì)穩(wěn)定的抽象層见芹,而將不同的實(shí)現(xiàn)行為移至具體的實(shí)現(xiàn)層中完成剂娄。

3.里氏替換原則

  • 所有引用基類(父類)的地方必須能透明地使用其子類的對(duì)象。
  • 在軟件中將一個(gè)基類對(duì)象替換成它的子類對(duì)象玄呛,程序?qū)⒉粫?huì)產(chǎn)生任何錯(cuò)誤和異常阅懦,反過(guò)來(lái)則不成立,如果一個(gè)軟件實(shí)體使用的是一個(gè)子類對(duì)象的話徘铝,那么它不一定能夠使用基類對(duì)象耳胎。
  • 里氏代換原則是實(shí)現(xiàn)開閉原則的重要方式之一,由于使用基類對(duì)象的地方都可以使用子類對(duì)象惕它,因此在程序中盡量使用基類類型來(lái)對(duì)對(duì)象進(jìn)行定義怕午,而在運(yùn)行時(shí)再確定其子類類型,用子類對(duì)象來(lái)替換父類對(duì)象淹魄。

4.依賴倒置原則

  • 抽象不應(yīng)該依賴于細(xì)節(jié)郁惜,細(xì)節(jié)應(yīng)當(dāng)依賴于抽象。換言之揭北,要針對(duì)接口編程扳炬,而不是針對(duì)實(shí)現(xiàn)編程
  • 在實(shí)現(xiàn)依賴倒轉(zhuǎn)原則時(shí),我們需要針對(duì)抽象層編程搔体,而將具體類的對(duì)象通過(guò)依賴注入(DependencyInjection, DI)的方式注入到其他對(duì)象中恨樟,依賴注入是指當(dāng)一個(gè)對(duì)象要與其他對(duì)象發(fā)生依賴關(guān)系時(shí),通過(guò)抽象來(lái)注入所依賴的對(duì)象疚俱。常用的注入方式有三種劝术,分別是:構(gòu)造注入,設(shè)值注入(Setter注入)和接口注入。構(gòu)造注入是指通過(guò)構(gòu)造函數(shù)來(lái)傳入具體類的對(duì)象养晋,設(shè)值注入是指通過(guò)Setter方法來(lái)傳入具體類的對(duì)象衬吆,而接口注入是指通過(guò)在接口中聲明的業(yè)務(wù)方法來(lái)傳入具體類的對(duì)象。這些方法在定義時(shí)使用的是抽象類型绳泉,在運(yùn)行時(shí)再傳入具體類型的對(duì)象逊抡,由子類對(duì)象來(lái)覆蓋父類對(duì)象。
  • 開閉原則是目標(biāo)零酪,里氏代換原則是基礎(chǔ)冒嫡,依賴倒轉(zhuǎn)原則是手段

5.接口隔離原則

  • 使用多個(gè)專門的接口,而不使用單一的總接口四苇,即客戶端不應(yīng)該依賴那些它不需要的接口孝凌。

6.合成復(fù)用原則

  • 盡量使用對(duì)象組合,而不是繼承來(lái)達(dá)到復(fù)用的目的月腋。
  • 通過(guò)繼承來(lái)進(jìn)行復(fù)用的主要問(wèn)題在于繼承復(fù)用會(huì)破壞系統(tǒng)的封裝性蟀架,因?yàn)槔^承會(huì)將基類的實(shí)現(xiàn)細(xì)節(jié)暴露給子類,由于基類的內(nèi)部細(xì)節(jié)通常對(duì)子類來(lái)說(shuō)是可見的榆骚,所以這種復(fù)用又稱“白箱”復(fù)用片拍,如果基類發(fā)生改變,那么子類的實(shí)現(xiàn)也不得不發(fā)生改變寨躁;從基類繼承而來(lái)的實(shí)現(xiàn)是靜態(tài)的穆碎,不可能在運(yùn)行時(shí)發(fā)生改變,沒(méi)有足夠的靈活性

7.迪米特法則

  • 一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少地與其他實(shí)體發(fā)生相互作用
  • 應(yīng)該盡量減少對(duì)象之間的交互职恳,如果兩個(gè)對(duì)象之間不必彼此直接通信所禀,那么這兩個(gè)對(duì)象就不應(yīng)當(dāng)發(fā)生任何直接的相互作用,如果其中的一個(gè)對(duì)象需要調(diào)用另一個(gè)對(duì)象的某一個(gè)方法的話放钦,可以通過(guò)第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用色徘。簡(jiǎn)言之,就是通過(guò)引入一個(gè)合理的第三者來(lái)降低現(xiàn)有對(duì)象之間的耦合度操禀。

二褂策、設(shè)計(jì)模式的分類

  • 創(chuàng)建型模式(5種):工廠方法模式,抽象工廠模式颓屑,建造者模式斤寂,原型模式,單例模式揪惦。
  • 結(jié)構(gòu)型模式(7種):適配器模式遍搞,裝飾器模式,代理模式器腋,外觀模式溪猿,橋接模式钩杰,組合模式,享元模式诊县。
  • 行為型模式(11種):策略模式讲弄、模板方法模式、觀察者模式依痊、迭代子模式避除、責(zé)任鏈模式、命令模式抗悍、備忘錄模式驹饺、狀態(tài)模式钳枕、訪問(wèn)者模式缴渊、中介者模式、解釋器模式鱼炒。

創(chuàng)建型模式

1. 工廠模式(Factory Method)

interface food{}

class A implements food{}
class B implements food{}
class C implements food{}
public class StaticFactory {
    private StaticFactory(){}
    
    public static food getA(){  return new A(); }
    public static food getB(){  return new B(); }
    public static food getC(){  return new C(); }
}

class Client{
    //客戶端代碼只需要將相應(yīng)的參數(shù)傳入即可得到對(duì)象
    //用戶不需要了解工廠類內(nèi)部的邏輯衔沼。
    public void get(String name){
        food x = null ;
        if ( name.equals("A")) {
            x = StaticFactory.getA();
        }else if ( name.equals("B")){
            x = StaticFactory.getB();
        }else {
            x = StaticFactory.getC();
        }
    }
}

2. 抽象工廠模式(Abstract Factory)

  • 一個(gè)基礎(chǔ)接口定義了功能,每個(gè)實(shí)現(xiàn)接口的子類就是產(chǎn)品昔瞧,然后定義一個(gè)工廠接口指蚁,實(shí)現(xiàn)了工廠接口的就是工廠,這時(shí)候自晰,接口編程的優(yōu)點(diǎn)就出現(xiàn)了凝化,我們可以新增產(chǎn)品類(只需要實(shí)現(xiàn)產(chǎn)品接口),只需要同時(shí)新增一個(gè)工廠類酬荞,客戶端就可以輕松調(diào)用新產(chǎn)品的代碼搓劫。
  • 抽象工廠的靈活性就體現(xiàn)在這里,無(wú)需改動(dòng)原有的代碼混巧,畢竟對(duì)于客戶端來(lái)說(shuō)枪向,靜態(tài)工廠模式在不改動(dòng)StaticFactory類的代碼時(shí)無(wú)法新增產(chǎn)品,如果采用了抽象工廠模式咧党,就可以輕松的新增拓展類秘蛔。
interface food{}

class A implements food{}
class B implements food{}

interface produce{ food get();}

class FactoryForA implements produce{
    @Override
    public food get() {
        return new A();
    }
}
class FactoryForB implements produce{
    @Override
    public food get() {
        return new B();
    }
}
public class AbstractFactory {
    public void ClientCode(String name){
        food x= new FactoryForA().get();
        x = new FactoryForB().get();
    }
}

3. 建造者模式(Builder)

  • 當(dāng)一個(gè)類的構(gòu)造函數(shù)參數(shù)個(gè)數(shù)超過(guò)4個(gè),而且這些參數(shù)有些是可選的參數(shù)傍衡,考慮使用構(gòu)造者模式深员。
public class Builder {

    static class Student{
        String name = null ;
        int number = -1 ;
        String sex = null ;
        int age = -1 ;
        String school = null ;
     //構(gòu)建器,利用構(gòu)建器作為參數(shù)來(lái)構(gòu)建Student對(duì)象
        static class StudentBuilder{
            String name = null ;
            int number = -1 ;
            String sex = null ;
            int age = -1 ;
            String school = null ;
            public StudentBuilder setName(String name) {
                this.name = name;
                return  this ;
            }

            public StudentBuilder setNumber(int number) {
                this.number = number;
                return  this ;
            }

            public StudentBuilder setSex(String sex) {
                this.sex = sex;
                return  this ;
            }

            public StudentBuilder setAge(int age) {
                this.age = age;
                return  this ;
            }

            public StudentBuilder setSchool(String school) {
                this.school = school;
                return  this ;
            }
            public Student build() {
                return new Student(this);
            }
        }

        public Student(StudentBuilder builder){
            this.age = builder.age;
            this.name = builder.name;
            this.number = builder.number;
            this.school = builder.school ;
            this.sex = builder.sex ;
        }
    }

    public static void main( String[] args ){
        Student a = new Student.StudentBuilder().setAge(13).setName("LiHua").build();
        Student b = new Student.StudentBuilder().setSchool("sc").setSex("Male").setName("ZhangSan").build();
    }
}

4. 原型模式(Protype)

  • 在原型模式中我們可以利用過(guò)一個(gè)原型對(duì)象來(lái)指明我們所要?jiǎng)?chuàng)建對(duì)象的類型蛙埂,然后通過(guò)復(fù)制這個(gè)對(duì)象的方法來(lái)獲得與該對(duì)象一模一樣的對(duì)象實(shí)例倦畅。
  • 深拷貝、淺拷貝
深拷貝
public class Prototype implements Cloneable{

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected Object clone()   {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }finally {
            return null;
        }
    }

    public static void main ( String[] args){
        Prototype pro = new Prototype();
        Prototype pro1 = (Prototype)pro.clone();
    }
}
  • Object類提供了一個(gè)clone()方法箱残,該方法可以將一個(gè)java對(duì)象復(fù)制一份滔迈,因此在java中可以直接使用clone()方法來(lái)復(fù)制一個(gè)對(duì)象止吁。但是需要實(shí)現(xiàn)clone的Java類必須要實(shí)現(xiàn)一個(gè)接口:Cloneable.該接口表示該類能夠復(fù)制且具體復(fù)制的能力,如果不實(shí)現(xiàn)該接口而直接調(diào)用clone()方法會(huì)拋出CloneNotSupportedException異常燎悍。

5. 單例模式(Singleton)

1). 餓漢法
  • 在第一次引用該類的時(shí)候就創(chuàng)建對(duì)象實(shí)例敬惦,而不管實(shí)際是否需要?jiǎng)?chuàng)建。
public class Singleton {  
    private static Singleton instance = new Singleton();  

    private Singleton (){}  

    public static Singleton getInstance() {  
      return instance;  
    }  

}  
2). 單線程寫法
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
      return instance;  
    }  
} 
3). 考慮線程安全的寫法
public class Singleton {  
    private static Singleton instance;  

    private Singleton (){}  

    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
      return instance;  
    }  

}  
4). 兼顧線程安全和效率的寫法

雙重校驗(yàn)鎖

public class Singleton {  
    private volatile static Singleton singleton;  

    private Singleton (){}  

    public static Singleton getSingleton() {  
      if (singleton == null) {  
            synchronized (Singleton.class) {  
                 if (singleton == null) {  
                        singleton = new Singleton();  
                  }  
            }  
        }  
      return singleton;  
    }  
}  
5). 靜態(tài)內(nèi)部類法
//Initialization on Demand Holder
public class Singleton{
       private  Singleton(){
       }

       private static class HolderClass{
              private final static Singleton  instance = new Singleton();
       }

       public static Singleton getInstance(){
              return HolderClass.instance;
       }

       public  static void main(String args[]){
              Singleton  s1, s2;
        s1 = Singleton.getInstance();
              s2 = Singleton.getInstance();
              System.out.println(s1==s2);
       }
}
  • 由于靜態(tài)單例對(duì)象沒(méi)有作為Singleton的成員變量直接實(shí)例化谈山,因此類加載時(shí)不會(huì)實(shí)例化Singleton俄删,第一次調(diào)用getInstance()時(shí)將加載內(nèi)部類HolderClass,在該內(nèi)部類中定義了一個(gè)static類型的變量instance奏路,此時(shí)會(huì)首先初始化這個(gè)成員變量畴椰,由Java虛擬機(jī)來(lái)保證其線程安全性,確保該成員變量只能初始化一次鸽粉。由于getInstance()方法沒(méi)有任何線程鎖定斜脂,因此其性能不會(huì)造成任何影響。
  • 通過(guò)使用IoDH触机,我們既可以實(shí)現(xiàn)延遲加載帚戳,又可以保證線程安全,不影響系統(tǒng)性能儡首,不失為一種最好的Java語(yǔ)言單例模式實(shí)現(xiàn)方式(其缺點(diǎn)是與編程語(yǔ)言本身的特性相關(guān)片任,很多面向?qū)ο笳Z(yǔ)言不支持IoDH)
6). 枚舉寫法
  • 這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問(wèn)題蔬胯,而且還能防止反序列化重新創(chuàng)建新的對(duì)象
public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}  

結(jié)構(gòu)型模式

1. 適配器模式(Adapter)

  • 適配器就是一種適配中間件对供,它存在于不匹配的二者之間,用于連接二者氛濒,將不匹配變得匹配产场,簡(jiǎn)單點(diǎn)理解就是平常所見的轉(zhuǎn)接頭,轉(zhuǎn)換器之類的存在泼橘。
1). 類適配器模式
  • 當(dāng)我們要訪問(wèn)的接口A中沒(méi)有我們想要的方法 涝动,卻在另一個(gè)接口B中發(fā)現(xiàn)了合適的方法,我們又不能改變?cè)L問(wèn)接口A
  • 原理:通過(guò)繼承來(lái)實(shí)現(xiàn)適配器功能炬灭。
public interface Ps2 {
    void isPs2();
}

public interface Usb {
    void isUsb();
}

public class Usber implements Usb {
    @Override
    public void isUsb() {
        System.out.println("USB口");
    }
}

public class Adapter extends Usber implements Ps2 {
    @Override
    public void isPs2() {
        isUsb();
    }
}

public class Clienter {
    public static void main(String[] args) {
        Ps2 p = new Adapter();
        p.isPs2();
    }
}
2). 對(duì)象適配
  • 原理:通過(guò)組合來(lái)實(shí)現(xiàn)適配器功能醋粟。
public class Adapter implements Ps2 {
    private Usb usb;
    public Adapter(Usb usb){
        this.usb = usb;
    }
    @Override
    public void isPs2() {
        usb.isUsb();
    }
}

public class Clienter {
    public static void main(String[] args) {
        Ps2 p = new Adapter(new Usber());
        p.isUsb();
    }
}
3). 接口適配器模式
  • 當(dāng)存在這樣一個(gè)接口,其中定義了N多的方法重归,而我們現(xiàn)在卻只想使用其中的一個(gè)到幾個(gè)方法米愿,如果我們直接實(shí)現(xiàn)接口,那么我們要對(duì)所有的方法進(jìn)行實(shí)現(xiàn)鼻吮,會(huì)導(dǎo)致這個(gè)類變得臃腫育苟,調(diào)用也不方便,這時(shí)我們可以使用一個(gè)抽象類作為中間件椎木,即適配器违柏,用這個(gè)抽象類實(shí)現(xiàn)接口博烂,而在抽象類中所有的方法都進(jìn)行置空,那么我們?cè)趧?chuàng)建抽象類的繼承類漱竖,而且重寫我們需要使用的那幾個(gè)方法即可禽篱。
  • 原理:通過(guò)抽象類來(lái)實(shí)現(xiàn)適配
public interface A {
    void a();
    void b();
    void c();
    void d();
    void e();
    void f();
}

public abstract class Adapter implements A {
    public void a(){}
    public void b(){}
    public void c(){}
    public void d(){}
    public void e(){}
    public void f(){}
}

public class Ashili extends Adapter {
    public void a(){
        System.out.println("實(shí)現(xiàn)A方法被調(diào)用");
    }
    public void d(){
        System.out.println("實(shí)現(xiàn)d方法被調(diào)用");
    }
}

public class Clienter {
    public static void main(String[] args) {
        A a = new Ashili();
        a.a();
        a.d();
    }
}

2.裝飾模式

  • 裝飾器模式在不影響各個(gè)ConcreteComponent核心價(jià)值的同時(shí),添加了他特有的裝飾效果馍惹,具備非常好的通用性
//基礎(chǔ)接口
public interface Component {
    
    public void biu();
}
//具體實(shí)現(xiàn)類
public class ConcretComponent implements Component {

    public void biu() {
        
        System.out.println("biubiubiu");
    }
}
//裝飾類
public class Decorator implements Component {

    public Component component;
    
    public Decorator(Component component) {
        
        this.component = component;
    }
    
    public void biu() {
        
        this.component.biu();
    }
}
//具體裝飾類
public class ConcreteDecorator extends Decorator {

    public ConcreteDecorator(Component component) {

        super(component);
    }

    public void biu() {
        
        System.out.println("ready?go!");
        this.component.biu();
    }
}
//使用裝飾器
  Component component = new ConcreteDecorator(new ConcretComponent());
  component.biu();

  //console:
  ready?go!
  biubiubiu

3. 代理模式

  • 通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.
  • 這里使用到編程中的一個(gè)思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需改修改,可以通過(guò)代理的方式來(lái)擴(kuò)展該方法
  • 代理模式的關(guān)鍵點(diǎn)是:代理對(duì)象與目標(biāo)對(duì)象.代理對(duì)象是對(duì)目標(biāo)對(duì)象的擴(kuò)展,并會(huì)調(diào)用目標(biāo)對(duì)象
1).靜態(tài)代理
  • 靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類.
/**
 * 接口
 */
public interface IUserDao {

    void save();
}
/**
 * 接口實(shí)現(xiàn)
 * 目標(biāo)對(duì)象
 */
public class UserDao implements IUserDao {
    public void save() {
        System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
    }
}
/**
 * 代理對(duì)象,靜態(tài)代理
 */
public class UserDaoProxy implements IUserDao{
    //接收保存目標(biāo)對(duì)象
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target=target;
    }

    public void save() {
        System.out.println("開始事務(wù)...");
        target.save();//執(zhí)行目標(biāo)對(duì)象的方法
        System.out.println("提交事務(wù)...");
    }
}
/**
 * 測(cè)試類
 */
public class App {
    public static void main(String[] args) {
        //目標(biāo)對(duì)象
        UserDao target = new UserDao();

        //代理對(duì)象,把目標(biāo)對(duì)象傳給代理對(duì)象,建立代理關(guān)系
        UserDaoProxy proxy = new UserDaoProxy(target);

        proxy.save();//執(zhí)行的是代理的方法
    }
}
  • 可以做到在不修改目標(biāo)對(duì)象的功能前提下,對(duì)目標(biāo)功能擴(kuò)展躺率。
  • 缺點(diǎn): 因?yàn)榇韺?duì)象需要與目標(biāo)對(duì)象實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類,類太多.同時(shí),一旦接口增加方法,目標(biāo)對(duì)象與代理對(duì)象都要維護(hù).
2). 動(dòng)態(tài)代理
  • 特點(diǎn):
    1>. 代理對(duì)象,不需要實(shí)現(xiàn)接口
    2>. 代理對(duì)象的生成,是利用JDK的API,動(dòng)態(tài)的在內(nèi)存中構(gòu)建代理對(duì)象(需要我們指定創(chuàng)建代理對(duì)象/目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型)
    3>. 動(dòng)態(tài)代理也叫做:JDK代理,接口代理
/**
 * 創(chuàng)建動(dòng)態(tài)代理對(duì)象
 * 動(dòng)態(tài)代理不需要實(shí)現(xiàn)接口,但是需要指定接口類型
 */
public class ProxyFactory{

    //維護(hù)一個(gè)目標(biāo)對(duì)象
    private Object target;
    public ProxyFactory(Object target){
        this.target=target;
    }

   //給目標(biāo)對(duì)象生成代理對(duì)象
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("開始事務(wù)2");
                        //執(zhí)行目標(biāo)對(duì)象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事務(wù)2");
                        return returnValue;
                    }
                }
        );
    }

}
/**
 * 測(cè)試類
 */
public class App {
    public static void main(String[] args) {
        // 目標(biāo)對(duì)象
        IUserDao target = new UserDao();
        // 【原始的類型 class cn.itcast.b_dynamic.UserDao】
        System.out.println(target.getClass());

        // 給目標(biāo)對(duì)象,創(chuàng)建代理對(duì)象
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        // class $Proxy0   內(nèi)存中動(dòng)態(tài)生成的代理對(duì)象
        System.out.println(proxy.getClass());

        // 執(zhí)行方法   【代理對(duì)象】
        proxy.save();
    }
}

行為型模式

1. 模板方法模式

  • 模板方法模式在一個(gè)方法中定義一個(gè)算法的骨架万矾,而將一些步驟的實(shí)現(xiàn)延遲到子類中悼吱。模板方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法中某些步驟的具體實(shí)現(xiàn)良狈。
  • 注意幾點(diǎn):
  1. 保護(hù)抽象類中定義算法順序的方法不被子類修改后添。
  2. 分離可變及不可變部分,讓子類自己決定可變部分的實(shí)現(xiàn)们颜。
  3. 讓算法的具體實(shí)現(xiàn)對(duì)子類開放吕朵,對(duì)其他類關(guān)閉。
package com.singland.dp.template;

public abstract class DriveTemplate {
    
    public final void drive() {
        openDoor();
        startEngine();
        gear();
        go();
        if (music()) {
            mp3();
        }
        brake();
        stop();
    }
    
    protected abstract void openDoor();
    
    protected void startEngine() {
        System.out.println("engine started !");
    }
    
    protected abstract void gear();
    
    protected void go() {
        System.out.println("running...");
    }
    
    private void mp3() {
        System.out.println("music is good");
    }
    
    protected boolean music() {
        return false;
    }
    
    protected abstract void brake();
    
    protected void stop() {
        System.out.println("stopped !");
    }
}

package com.singland.dp.template;

public class SuzukiScross extends DriveTemplate {
    
    @Override
    protected void openDoor() {
        System.out.println("keyless entry");
    }

    @Override
    protected void gear() {
        System.out.println("gear with hand");
    }

    @Override
    protected void brake() {
        System.out.println("brake with foot");
    }

    @Override
    protected boolean music() {
        return true;
    }
}
  • JDBC代碼操作數(shù)據(jù)庫(kù)
package com.studentinfomgt.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;

public abstract class JdbcTemplate2 {
    
    @Autowired
    private DataSource dataSource;
    
    private Connection connection;
    
    protected PreparedStatement statement;
    
    protected ResultSet resultSet;
    
    public final void dbOperation(String sql, Object entity) throws SQLException {
        getStatement(sql);
        crud(entity);
        releaseResources();
    }

    protected void getStatement(String sql) throws SQLException {
        connection = dataSource.getConnection();
        this.statement = connection.prepareStatement(sql);
    }
    
    protected abstract void crud(Object entity) throws SQLException;
    
    private void releaseResources() throws SQLException {
        if (resultSet != null)
            resultSet.close();
        if (statement != null)
            statement.close();
        if (connection != null)
            connection.close();
    }
}
package com.studentinfomgt.dao;

import java.sql.SQLException;

import com.studentinfomgt.pojo.Student;

public class JdbcCreateEntity extends JdbcTemplate2 {
    
    @Override
    protected void crud(Object entity) throws SQLException {
        Student student = (Student) entity;
        statement.setString(1, student.getId());
        statement.setString(2, student.getStudentNumber());
        statement.setString(3, student.getFirstName());
        statement.setString(4, student.getLastName());
        statement.setString(5, student.getGender());
        statement.setInt(6, student.getAge());
        statement.setString(7, student.getClassName());
        statement.setString(8, student.getMajor());
        statement.execute();
    }
}

2. 訪問(wèn)者模式

  • 將作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作分離出來(lái)封裝成獨(dú)立的類窥突,使其在不改變數(shù)據(jù)結(jié)構(gòu)的前提下可以添加作用于這些元素的新的操作,為數(shù)據(jù)結(jié)構(gòu)中的每個(gè)元素提供多種訪問(wèn)方式硫嘶。它將對(duì)數(shù)據(jù)的操作與數(shù)據(jù)結(jié)構(gòu)進(jìn)行分離阻问,是行為類模式中最復(fù)雜的一種模式。
  • 訪問(wèn)者中的元素:
  1. 抽象訪問(wèn)者(Visitor)角色:定義一個(gè)訪問(wèn)具體元素的接口敞恋,為每個(gè)具體元素類對(duì)應(yīng)一個(gè)訪問(wèn)操作 visit() 甸鸟,該操作中的參數(shù)類型標(biāo)識(shí)了被訪問(wèn)的具體元素块攒。
  2. 具體訪問(wèn)者(ConcreteVisitor)角色:實(shí)現(xiàn)抽象訪問(wèn)者角色中聲明的各個(gè)訪問(wèn)操作,確定訪問(wèn)者訪問(wèn)一個(gè)元素時(shí)該做什么刨秆。
  3. 抽象元素(Element)角色:聲明一個(gè)包含接受操作 accept() 的接口,被接受的訪問(wèn)者對(duì)象作為 accept() 方法的參數(shù)忆畅。
  4. 具體元素(ConcreteElement)角色:實(shí)現(xiàn)抽象元素角色提供的 accept() 操作衡未,其方法體通常都是 visitor.visit(this) ,另外具體元素中可能還包含本身業(yè)務(wù)邏輯的相關(guān)操作家凯。
/**
 * 訪問(wèn)者接口
 */
public interface IVisitor {

    void visit(FreeCourse freeCourse);

    void visit(CodingCourse codingCourse);
}


/**
 * 訪問(wèn)者的具體實(shí)現(xiàn)
 */
public class Visitor implements IVisitor {
    @Override
    public void visit(FreeCourse freeCourse) {
        System.out.println("free course: " + freeCourse.getName());
    }

    @Override
    public void visit(CodingCourse codingCourse) {
        System.out.println("coding course: " + codingCourse.getName() + " price: " + codingCourse.getPrice());
    }
}

/**
 * 抽象類缓醋,課程
 */
public abstract class Course {

    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    abstract void accept(IVisitor visitor);

}

/**
 * 具體的實(shí)體類,免費(fèi)課程
 */
public class FreeCourse extends Course {


    @Override
    void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}

/**
 * 具體的實(shí)現(xiàn)類绊诲,實(shí)戰(zhàn)課程
 */
public class CodingCourse extends Course {

    private int price;

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}


/**
 * 測(cè)試類
 */
public class App {

    @Test
    public void test(){
        //初始化容器
        List<Course> courses = new ArrayList<>();

        //構(gòu)建課程實(shí)現(xiàn)
        FreeCourse freeCourse = new FreeCourse();
        freeCourse.setName("SpringMVC.");

        CodingCourse codingCourse = new CodingCourse();
        codingCourse.setName("Java design pattern.");
        codingCourse.setPrice(299);

        //裝填
        courses.add(freeCourse);
        courses.add(codingCourse);

        //訪問(wèn)
        for (Course course : courses) {
            course.accept(new Visitor());
        }
    }
}

  • 優(yōu)點(diǎn):
  1. 擴(kuò)展性好送粱。能夠在不修改對(duì)象結(jié)構(gòu)中的元素的情況下,為對(duì)象結(jié)構(gòu)中的元素添加新的功能掂之。
  2. 復(fù)用性好抗俄〈喽。可以通過(guò)訪問(wèn)者來(lái)定義整個(gè)對(duì)象結(jié)構(gòu)通用的功能,從而提高系統(tǒng)的復(fù)用程度动雹。靈活性好偎快。訪問(wèn)者模式將數(shù)據(jù)結(jié)構(gòu)與作用于結(jié)構(gòu)上的操作解耦,使得操作集合可相對(duì)自由地演化而不影響系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)洽胶。
  3. 符合單一職責(zé)原則晒夹。訪問(wèn)者模式把相關(guān)的行為封裝在一起,構(gòu)成一個(gè)訪問(wèn)者姊氓,使每一個(gè)訪問(wèn)者的功能都比較單一丐怯。
  • 缺點(diǎn):
  1. 增加新的元素類很困難。在訪問(wèn)者模式中翔横,每增加一個(gè)新的元素類读跷,都要在每一個(gè)具體訪問(wèn)者類中增加相應(yīng)的具體操作,這違背了“開閉原則”禾唁。
  2. 破壞封裝效览。訪問(wèn)者模式中具體元素對(duì)訪問(wèn)者公布細(xì)節(jié),這破壞了對(duì)象的封裝性荡短,具體元素變更比較麻煩丐枉。違反了依賴倒置原則。
  3. 訪問(wèn)者模式依賴了具體類掘托,而沒(méi)有依賴抽象類瘦锹。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市闪盔,隨后出現(xiàn)的幾起案子弯院,更是在濱河造成了極大的恐慌,老刑警劉巖泪掀,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件听绳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡异赫,警方通過(guò)查閱死者的電腦和手機(jī)椅挣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)祝辣,“玉大人贴妻,你說(shuō)我怎么就攤上這事◎保” “怎么了名惩?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)孕荠。 經(jīng)常有香客問(wèn)我娩鹉,道長(zhǎng)攻谁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任弯予,我火速辦了婚禮戚宦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锈嫩。我一直安慰自己受楼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布呼寸。 她就那樣靜靜地躺著艳汽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪对雪。 梳的紋絲不亂的頭發(fā)上河狐,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音瑟捣,去河邊找鬼馋艺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛迈套,可吹牛的內(nèi)容都是我干的捐祠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼交汤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雏赦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起芙扎,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎填大,沒(méi)想到半個(gè)月后戒洼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡允华,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年圈浇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片靴寂。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡磷蜀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出百炬,到底是詐尸還是另有隱情褐隆,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布剖踊,位于F島的核電站庶弃,受9級(jí)特大地震影響衫贬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歇攻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一固惯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缴守,春花似錦葬毫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至鸡捐,卻和暖如春栈暇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箍镜。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工源祈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人色迂。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓香缺,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親歇僧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子图张,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容