很久不見,Java設計模式

圖片描述

引子

設計模式是不少程序員總結出來的最佳實踐。曾經在剛開始寫項目的時候學習過設計模式,在開發過程當中,也主動或者被動的使用過。如今寫代碼雖然說不會特地明確在用哪一種設計模式,但潛移默化的寫出來公認的最佳實踐代碼,畢竟看的比較清爽。爲何再看一遍設計模式,主要有幾個緣由:第一,不少優秀的源碼基本都使用了設計模式,明確設計模式可以更好的看源碼。第二,不少中間件設計理念也是基於設計模式的,還有其餘的語言,都有本身的設計最佳實踐。對於我來講,設計模式始於java,不止於java。第三,有了這種規範,能夠更好的和他人溝通,言簡意賅。java

設計模式原則

不少優秀的文章和書籍都講的很明白了,我說下本身的體會。
1.單一職責原則,就是一個類只負責作一件事情。這樣就能夠作到解耦合的效果,讓代碼看起來比較清爽,也體現了java的封裝性。還有個原則叫迪米特法則,就是一個對象對另外一個對象有儘可能少的瞭解,說的也是解耦合的事情。
2.里氏替換原則和依賴致使原則,說的是繼承的事情。父類能夠作的事情,子類均可以去作,子類能夠儘可能去依賴父類去作事情;可是反過來,父類不能依賴子類去作一些事情。體現了java的繼承特性。
3.接口隔離原則,接口也應該儘量的隔離開來。其實類寫多了,的確耦合性低,爲了讓他們交流起來,用的最多的就是接口,畢竟只須要知道作什麼,怎麼作,去訪問那個具體的類吧。
4.開閉原則,對修改關閉,對拓展開放。就是代碼須要有很好的延展性,對原有代碼結構不能破壞。node

建立者模式

建立者模式就是爲了用優雅的方式建立咱們使用的類。mysql

1.簡單工廠模式

這個用的比較少,就是有個工廠,告訴你我要什麼東西,你造好了給我就行。好比說:程序員

public interface Ball {
    public String create();
}
public class Soccer implements Ball {
    @Override
    public String create() {
        return "give you a soccer";
    }
}
public class BasketBall implements Ball {
    @Override
    public String create() {
        return "give you a basketBall";
    }
}
public class EasyBallFactory  {
    public static Ball createBall(String name){
        if (name.equals("basketball")){
            return new BasketBall();
        }else if(name.equals("soccer")){
            return new Soccer();
        }else {
            return null;
        }
    }
    public static void main(String[] args) {
        Ball soccer = EasyBallFactory.createBall("soccer");
        Ball basketBall = EasyBallFactory.createBall("basketball");
        System.out.println(soccer.create()); //give you a soccer
        System.out.println(basketBall.create()); //give you a basketBall
    }
}

2.工廠模式

這個其實和簡單工廠模式差不太多,就是將工廠繼續拆分,好比說剛剛EasyBallFactory是一個總工廠,咱們如今拆分紅SoccerFactory和BasketBallFactory分別生產足球和籃球。某個工廠內部能夠根據需求生產不一樣的產品,好比說soccer能夠生產不一樣大小的出來。算法

public interface BallFactory {
    public Ball create();
}
public class SoccerFactory implements BallFactory {
    @Override
    public Ball create() {
        //do something
        return null;
    }
}
public class BasketBallFactory implements BallFactory {
    @Override
    public Ball create() {
        //do something
        return null;
    }
}

3.抽象工廠模式

抽象工廠模式主要設計產品組的概念,就是某一個工廠生產出配套的一系列產品。例如,在生產足球的同時,SoccerFactory還能夠生產與之配套的足球雜誌。sql

public interface Journal {
    public String create();
}
public class SoccerJournal implements Journal{
    @Override
    public String create() {
        return "give you a Soccer Journal...";
    }
}
public class SoccerFactory implements BallFactory {
    @Override
    public Ball create() {
        return new Soccer();
    }
    public Journal createJournal(){
        return new SoccerJournal();
    }
}

4.單例模式

單例模式有不少種形式,最佳實踐應該是兩重判斷,保證只new出來一個。單例能夠說是很是廣泛的設計模式了。單例就是指在服務容器的生命週期中只能有這麼一個。好比說Servlet、Spring中注入的Bean等等都是單例的。數據庫

public class ShiroUtils {
    public  static Session session;
    public static Session getSession() {
        if (session == null){
            synchronized(ShiroUtils.class){
                if (session == null){
                    session = SecurityUtils.getSubject().getSession();
                }
            }
        }
        return session;
    }
}

5.建造者模式

將一個複雜對象分佈建立。若是一個超大的類的屬性特別多,咱們能夠把屬性分門別類,不一樣屬性組成一個稍微小一點的類,再把好幾個稍微小點的類竄起來。比方說一個電腦,能夠分紅不一樣的稍微小點的部分CPU、主板、顯示器。CPU、主板、顯示器分別有更多的組件,再也不細分。編程

@Data
public class Computer{
    private CPU cpu;//cpu 是個接口,有不一樣實現如InterCPU AMDCPU 等等
    private MainBoard mainBoard;//mainBoard 是個接口,有不一樣的實現
    private DisPlayer disPlayer;//disPlayer 是個接口,有不一樣的實現
}
public abstract class Builder { 
    abstract void buildCPU(); 
    abstract void buildMainBoard(); 
    abstract void buildDisPlayer(); 
    abstract Computer createComputer(); 
}
public class XiaoMiBuilder extends Builder{
    private Computer computer = new Computer();
    @Override
    void buildCPU() {
        computer.setCpu(new InterCPU());
    }
    @Override
    void buildMainBoard() {
        computer.setMainBoard(new AMainBoard());
    }
    @Override
    void buildDisPlayer() {
        computer.setDisPlayer(new ADisPlayer());
    }
    @Override
    Computer createComputer() {
        return computer;
    }
}

SpringBoot實現了0配置,幾乎全部的配置都寫到了java代碼中,大量的配置不得不讓配置類採用建造者模式,這樣層次比較清晰。設計模式

6.原型模式

原型模式用的比較少,用於建立重複對象。須要實現Cloneable 能夠選擇重寫clone()方法。clone分爲淺克隆和深克隆。淺克隆只是克隆引用,對象仍是一個。深克隆是對象也新建立了一個,以下:緩存

@Data
@Builder
public class User implements Cloneable{
    private String name;
    private int age;
    @Override
    protected User clone() throws CloneNotSupportedException {
        return new User(this.name,this.age);
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        User user1 = new User("pjjlt",25);
        User user2 = user1.clone();
        user1.setAge(18);
        System.out.println(user2.getAge()); //25
    }
}

結構型模式

上面的設計模式能夠幫助咱們很是優雅的建立出來對象,下面看幾個對象關係之間的模型。

7.代理模式

Spring的AOP用的是動態代理,何爲動態不看了,用過Spring的小夥伴都知道吧。單純看一下最基礎代理模式是什麼樣的。代理就是,一個對象輔助另外一個對象去作某件事,同時還能夠增長一點輔助功能。例如,你買車,的確是你花錢把車買到了,可是你不可能直接去和廠家談吧,你應該經過4S店購買,同時4S店幫助你入保險扣稅等操做,最終你才獲得了你想要的車。

public interface Buy {
    public void buyCar();
}
public class People implements Buy {
    @Override
    public void buyCar() {
        System.out.println("you get a car");
    }
}
public class ProxyPeople implements Buy{
    private People people;
    public ProxyPeople(People people){
        this.people=people;
    }
    @Override
    public void buyCar() {
        System.out.println("4s店幫你納稅、上保險...");
        people.buyCar();
    }
    public static void main(String[] args) {
        Buy buy = new ProxyPeople(new People());
        buy.buyCar();
    }
}
//輸出:
4s店幫你納稅、上保險...
you get a car

8.適配器模式

適配器,顧名思義,是讓兩個不兼容的東西能夠一塊兒工做。例如插座的電源是220V,手機直接給他220V 50HZ的交流電我相信通常都會直接炸了(除了諾基亞...)手機充電器就進行了適配,將電壓變小,交流電變成直流電。除了這種須要改變屬性的操做(比較好說,不舉例子了),適配器還用於在接口繼承方面。假設一個頂級接口有一大堆方法須要實現類實現,我新寫了個類只是想選擇的實現一兩個接口,那其餘的方法我是否是都須要實現一下,即便是空實現(單純實現,不進行任何邏輯操做),這是咱們就須要一個適配器類,空實現那些方法,個人新類只須要繼承這個適配器類就行了,要是想實現某個方法,只須要重寫掉配置類中對應的方法就好。這種模式基本都會用到,畢竟誰的代碼還沒個頂級接口啊。

public interface ATopIntf {
    public void one();
    public void two();
    public void three();
}
public class Adapter implements ATopIntf {
    @Override
    public void one() { }
    @Override
    public void two() { }
    @Override
    public void three() { }
}
public class You extends Adapter {
    @Override
    public void one() {
        super.one();
        System.out.println("one");
    }
}

9.橋接模式

就是用於抽象化和實現化的解耦。又是解耦,貌似設計模式就是教咱們如何優雅的解耦。提升了代碼的拓展性,而且能夠實現代碼的動態切換。
最開始的Ball、Soccer、BasketBall接着用,增長新的類

public  class BallCut {
    private Ball ball;
    public Ball getBall() {
        return ball;
    }
    public void setBall(Ball ball) {
        this.ball = ball;
    }
    public void create(){
         System.out.println(ball.create());
    }
    public static void main(String[] args) {
        BallCut ballCut = new BallCut();
        ballCut.setBall(new Soccer());
        ballCut.create(); //give you a soccer
        ballCut.setBall(new BasketBall());
        ballCut.create(); //give you a basketBall
    }
}

10.裝飾模式

一個裝飾類,在原來類的基礎上增長一點功能。是否是和代理模式很像,我甚至能夠將整個代碼搬過來照樣能夠說的通的。這兩個模式意思上有點差異,代理模式是原對象作不了那件事,必須讓代理對象去作,主導側重於代理對象,好比說買車。裝飾模式是說,就是讓原對象直接去作這件事,只是功能上加強一點,主導在於原對象。好比說炒菜的時候撒點鹽。

11.外觀模式

又稱門面模式,就是一個門面,一個操做無需讓對象知道其內部實現的複雜度,儘可能讓用戶感知到是很是簡單的。這就是爲何咱們controller層儘可能(或者說必定)少些業務邏輯,讓controller層只是起到一個傳參和通用性參數校驗的功能,剩下的全交給service去作吧。咱們還須要在代碼中不斷將「長得」特別長的代碼封裝成一個方法,「讓到處都有好看的外觀」。看一下咱們曾寫過的代碼,這裏只起到了傳參的做用,究竟這個足球是怎麼建立出來的,客戶端沒必要擔憂。

public static void main(String[] args) {
        Ball soccer = EasyBallFactory.createBall("soccer");
        System.out.println(soccer.create()); //give you a soccer
    }

12.組合模式

組合模式是將存在某種包含關係的數據組織在一塊兒,典型的例子就是樹狀結構。例如菜單功能,一個菜單除了本身該有的屬性,還可能包含子菜單,建立的時候可使用遞歸的方法。

@Data
public class Menu{
    private String name;
    private int type;
    private List<Menu> childMenus;
}

13.享元模式

享元模式儘量的讓用戶複用已經有的對象,從而避免形成反覆建立對象的資源浪費。首先就會想到數據庫鏈接池還有String常量池,延伸一下,幾乎全部和緩存有關的代碼,多少都會用到享元模式。享元模式要求大部分的對象能夠外部化。這邊要說兩個概念,享元模式對象的屬性能夠分爲兩個部分,內部狀態和外部狀態,內部狀態是指不會隨環境而改變的值,好比說我的信息,外部狀態是指隨環境改變的值,不能進行共享的信息,如某大學生選修的課程。

public abstract class Flyweight {
    //內部狀態
    private String name;
    private String age;
    //外部狀態
    private final String subject;
    protected Flyweight(String subject) {
        this.subject = subject;
    }
    //行爲
    public abstract void exam();
    public String getSubject() {
        return subject;
    }
}
public class RealFlyweight extends Flyweight {
    @Override
    public void exam() {
        System.out.println(this.getSubject()+" is examing...");
    }
    public RealFlyweight(String subject){
        super(subject);
    }
}
public class FlyweightFactory {
    //定義一個池子
    private static HashMap<String,Flyweight> pool= new HashMap();
    public static Flyweight getFlyweight(String subject){
        Flyweight flyweight =null;
        if (pool.containsKey(subject)){
            flyweight=pool.get(subject);
        }else {
            flyweight = new RealFlyweight(subject);
            pool.put(subject,flyweight);
        }
        return flyweight;
    }
    public static void main(String[] args) {
        System.out.println(pool.size());//0
        getFlyweight("math");
        System.out.println(pool.size());//1
        getFlyweight("english");
        System.out.println(pool.size());//2
        getFlyweight("math");
        System.out.println(pool.size());//2
    }
}

行爲型模式

建立了對象,對象之間有告終構關係,就要看下怎麼更加優雅的相互做用了。

14.策略模式

定義一組算法, 將每一個算法都封裝起來, 而且使它們之間能夠互換。能夠說是一組算法的封裝,根據客戶端給出的不一樣要求,進行不一樣的運算。好比下面這個簡易計算器。

public interface Strategy {
   public int doOperation(int num1, int num2);
}
public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}
public class OperationSubstract implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}
public class Context {
   private Strategy strategy;
   public Context(Strategy strategy){
      this.strategy = strategy;
   }
   public int executeStrategy(int num1, int num2){
      return strategy.doOperation(num1, num2);
   }
}
public class StrategyPatternDemo {
   public static void main(String[] args) {
      Context context = new Context(new OperationAdd());    
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
      context = new Context(new OperationSubstract());      
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
   }
}

15.觀察者模式

定義了一種一對多的依賴關係,當一個對象(被觀察者)狀態改變的時候,全部依賴於該對象的觀察者都會被通知,從而進行相關操做。不少中間件都依賴於觀察者模式,例如RabbitMQ,還有那些事件驅動模型(好像node就是)。下面舉個例子,被觀察者是監考老師,考試時間結束,通知全部觀察者學生上交試卷。

@Data
public class Student {
    private String name;
    public Student(String name){
        this.name=name;
    }
    public void dosomething(){
        System.out.println(getName()+"交卷了");
    }
}
public class Teacher {
    private Set<Student> students = new HashSet<>();
    public void addStudent(Student student){
        students.add(student);
    }
    public void removeStudent(Student student){
        students.remove(student);
    }
    public void doNotify(){
        for(Student student:students){
            student.dosomething();
        }
    }
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        Student student = new Student("張三");
        Student student1 = new Student("李四");
        teacher.addStudent(student);
        teacher.addStudent(student1);
        teacher.doNotify();
    }
}

16.責任鏈模式

責任鏈模式爲請求建立一個接收者對象的鏈,對發送者和接受者進行解耦合。filter鏈就是責任鏈模式。

public abstract class Handler {
    //下一個處理者
    private Handler nextHandler;
    public final Response handleMessage(Request request){
        Response response =null;
        if (this.getHandlerLevel().equals(request.getRequestLevel())){
            response = this.echo(request);
        }else {
            if (this.nextHandler!=null){
                //傳遞給下一個
                response = this.nextHandler.handleMessage(request);
            }else {
                response =new Response()
            }
        }
        return response;
    }
    public Handler getNextHandler() {
        return nextHandler;
    }
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }
    protected abstract Level getHandlerLevel();
    protected abstract Response echo(Request request);
}

17.模板方式模式

一個抽象類公開定義了執行它的方法的方式/模板。它的子類能夠按須要重寫方法實現,但調用將以抽象類中定義的方式進行。SpringBoot爲用戶封裝了不少繼承代碼,都用到了模板方式,例如那一堆XXXtemplate。

public abstract class DBTemplate {
    abstract void open();
    abstract void select();
    abstract void close();
    //一個搜索模板
    public final void selectTemplate(){
        open();
        select();
        close();
    }
}
public class MysqlDB extends DBTemplate {
    @Override
    void open() {
        System.out.println("Mysql open...");
    }
    @Override
    void select() {
        System.out.println("Mysql select...");
    }
    @Override
    void close() {
        System.out.println("Mysql close...");
    }

    public static void main(String[] args) {
        DBTemplate mysql = new MysqlDB();
        mysql.selectTemplate();
    }
}

18.狀態模式

簡單來講,就是一個對象有不一樣的狀態,根據狀態不一樣,可能有不一樣的行爲。

public interface State {
   public void doAction(Context context);
}
public class StartState implements State {
   public void doAction(Context context) {
      System.out.println("Player is in start state");
      context.setState(this); 
   }
   public String toString(){
      return "Start State";
   }
}
public class StopState implements State {
   public void doAction(Context context) {
      System.out.println("Player is in stop state");
      context.setState(this); 
   }
   public String toString(){
      return "Stop State";
   }
}
public class Context {
   private State state;
   public Context(){
      state = null;
   }
   public void setState(State state){
      this.state = state;     
   }
   public State getState(){
      return state;
   }
}
public class StatePatternDemo {
   public static void main(String[] args) {
      Context context = new Context();
      StartState startState = new StartState();
      startState.doAction(context);
      System.out.println(context.getState().toString());
      StopState stopState = new StopState();
      stopState.doAction(context);
      System.out.println(context.getState().toString());
   }
}

19.迭代器模式

提供一個方法,能夠順序訪問一個對象內部的各個元素,不須要知道內部構造。如今基本不多本身實現迭代器了,基本成熟的框架或者強大的JDK都會給出訪問的方法,好比說java中iterator。這樣作主要是進一步封裝對象內部的結構,讓行爲和結構想耦合。這個不舉例子了,用過iterator這個的小夥伴應該都清楚,就是不停的next,去訪問下一個元素。

20.命令模式

命令模式是將請求以命令的形式包裹在對象中,並傳遞給對象,調用對象尋找處處理該命令的合適的對象,並將該命令傳遞給相應的對象,該對象執行。簡單點說就是不一樣請求都封裝成一個對象,不一樣的請求調用不一樣的執行者。

//真正幹活的對象
public class Receiver {
    public void doSomething(){
        System.out.println("Receiver幹活");
    }
}
//命令對象
public abstract class Command {
    public abstract void exectue();
}
//命令實現類
public class ConcreteComand extends Command{
    //幹活那個
    private Receiver receiver;
    public ConcreteComand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void exectue() {
        this.receiver.doSomething();
    }

    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command =new ConcreteComand(receiver);
        command.exectue();//Receiver幹活
    }
}

21.備忘錄模式

至關於作一個快照,在不破壞對象自己結構的狀況下,記錄對象的一個狀態,合適的時候能夠恢復到這種狀態。數據庫作事務回滾的時候就用了這種方式。這裏須要注意的是,對象不與備忘錄自己耦合,而是跟備忘錄管理類耦合(就是List<備忘錄>),這個好理解,畢竟快照不止一個嘛。

@Data//備忘錄
public class Memento {
   private String state; 
}
@Data //某對象
public class Originator {
   private String state;
   public Memento saveStateToMemento(){
      return new Memento(state);
   }
   public void getStateFromMemento(Memento Memento){
      state = Memento.getState();
   }
}
//備忘錄管理類
public class CareTaker {
   private List<Memento> mementoList = new ArrayList<Memento>();
   public void add(Memento state){
      mementoList.add(state);
   }
   public Memento get(int index){
      return mementoList.get(index);
   }
   public static void main(String[] args) {
      Originator originator = new Originator();
      CareTaker careTaker = new CareTaker();
      originator.setState("State #1");
      originator.setState("State #2");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #3");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #4");
      System.out.println("Current State: " + originator.getState());    
      originator.getStateFromMemento(careTaker.get(0));
      System.out.println("First saved State: " + originator.getState());
      originator.getStateFromMemento(careTaker.get(1));
      System.out.println("Second saved State: " + originator.getState());
   }
}

22.訪問者模式

當對特定角色進行訪問的時候,須要經過訪問者進行訪問。一個對象不太方便被你直接訪問的時候,你須要將本身的引用交給訪問者,經過訪問者去訪問該對象。好比說,化學課,想看一個細胞結構,因爲肉眼沒法直接看到微觀世界的玩意,須要經過顯微鏡間接訪問。

23.中介者模式

下降對象或者說事物之間通信的複雜性,下降耦合。好比說分佈式系統中,不是須要實時反饋的操做,咱們無需直接對接,只需將想作的事告訴中間件,中間件告訴另一個系統。好比說,訪問(用戶點擊)一條新聞操做,同時須要記錄是誰訪問了什麼新聞,同時給新聞瀏覽次數加1,還要實時更新用戶喜愛...總之要更新n個數據庫表,甚至還要操做像ES,Mongo等多箇中間件數據。可是對於用戶來講,我只是作了一個點擊操做,但願獲得的結果就是看條新聞啊,你這麼多操做,搞這麼慢,用戶體驗不好啊,並且併發量也很低,那不如作成兩個小系統,A系統,拉取新聞,推送,並組裝一個信息扔給MQ中間件,ok,結束,用戶看到新聞。而後B系統監聽,獲得這個消息,進行各類更新,這裏,這個中間件就是咱們的中介。再好比說,MVC中的控制層就是展現層和模型層的中介。再好比說,下面這個聊天室:

public class ChatRoom {
   public static void showMessage(User user, String message){
      System.out.println(new Date().toString()
         + " [" + user.getName() +"] : " + message);
   }
}
@Data
public class User {
   private String name;
   public User(String name){
      this.name  = name;
   }
   public void sendMessage(String message){
      ChatRoom.showMessage(this,message);
   }
   public static void main(String[] args) {
      User robert = new User("Robert");
      User john = new User("John");
 
      robert.sendMessage("Hi! John!");
      john.sendMessage("Hello! Robert!");
   }
}

24.解釋器模式

構建一種翻譯方式,將某種語言或描述翻譯成咱們很好理解的語言或者描述。這裏很好理解的意思是看得懂,看的快。原本我也想舉什麼編譯器這種高大上的,將底層語言甚至機械語言和咱們使用的高級編程語言。後來想了想,其實Map就能夠看做一個很好的編譯器,key你能夠存放一個很是小的字符串,value理論上你能夠存聽任何東西,因此代碼就不寫了。

結束語

嗚呼,廢了好長的時間,終於把這些設計模式有回顧了一遍,感受對他們其中的某些有了更深入的理解吧。就把這篇文章看成本身的一個小字典吧,未來須要什麼看什麼,點看看一看,順即可以繼續豐富下內容。有些東西就該本身去記錄一下,畢竟好記性不如爛筆頭嘛。又快十一點了,睡覺...

相關文章
相關標籤/搜索