摘要:享元模式享元模式盡可能的讓用戶復用已經有的對象,從而避免造成反復創建對象的資源浪費。享元模式要求大部分的對象可以外部化。
引子
設計模式是很多程序員總結出來的最佳實踐。曾經在剛開始寫項目的時候學習過設計模式,在開發過程中,也主動或者被動的使用過。現在寫代碼雖說不會特意明確在用哪種設計模式,但潛移默化的寫出來公認的最佳實踐代碼,畢竟看的比較清爽。為什么再看一遍設計模式,主要有幾個原因:第一,很多優秀的源碼基本都使用了設計模式,明確設計模式能夠更好的看源碼。第二,很多中間件設計理念也是基于設計模式的,還有其他的語言,都有自己的設計最佳實踐。對于我來說,設計模式始于java,不止于java。第三,有了這種規范,可以更好的和他人溝通,言簡意賅。
設計模式原則很多優秀的文章和書籍都講的很明白了,我說下自己的體會。
1.單一職責原則,就是一個類只負責做一件事情。這樣就可以做到解耦合的效果,讓代碼看起來比較清爽,也體現了java的封裝性。還有個原則叫迪米特法則,就是一個對象對另一個對象有盡量少的了解,說的也是解耦合的事情。
2.里氏替換原則和依賴導致原則,說的是繼承的事情。父類可以做的事情,子類都可以去做,子類可以盡量去依賴父類去做事情;但是反過來,父類不能依賴子類去做一些事情。體現了java的繼承特性。
3.接口隔離原則,接口也應該盡可能的隔離開來。其實類寫多了,的確耦合性低,為了讓他們交流起來,用的最多的就是接口,畢竟只需要知道做什么,怎么做,去訪問那個具體的類吧。
4.開閉原則,對修改關閉,對拓展開放。就是代碼需要有很好的延展性,對原有代碼結構不能破壞。
創建者模式就是為了用優雅的方式創建我們使用的類。
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還可以生產與之配套的足球雜志。
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 car8.適配器模式
適配器,顧名思義,是讓兩個不兼容的東西可以一起工作。例如插座的電源是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 List13.享元模式
享元模式盡可能的讓用戶復用已經有的對象,從而避免造成反復創建對象的資源浪費。首先就會想到數據庫連接池還有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行為型模式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 Set16.責任鏈模式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(); } }
責任鏈模式為請求創建一個接收者對象的鏈,對發送者和接受者進行解耦合。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 List22.訪問者模式mementoList = new ArrayList (); 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()); } }
當對特定角色進行訪問的時候,需要通過訪問者進行訪問。一個對象不太方便被你直接訪問的時候,你需要將自己的引用交給訪問者,通過訪問者去訪問該對象。比如說,化學課,想看一個細胞結構,由于肉眼無法直接看到微觀世界的玩意,需要通過顯微鏡間接訪問。
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理論上你可以存放任何東西,所以代碼就不寫了。
結束語嗚呼,廢了好長的時間,終于把這些設計模式有回顧了一遍,感覺對他們其中的某些有了更深刻的理解吧。就把這篇文章當作自己的一個小字典吧,將來需要什么看什么,點看看一看,順便可以繼續豐富下內容。有些東西就該自己去記錄一下,畢竟好記性不如爛筆頭嘛。又快十一點了,睡覺...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75301.html
摘要:屬性是此次響應的文本內容。我們可以通過屬性,指定這個事件的回調函數,對不同狀態進行不同處理。尤其是當狀態變為的時候,表示通信成功,這時回調函數就可以處理服務器傳送回來的數據。由于臨時的服務器維護或者過載,服務器當前無法處理請求。 XMLHttpRequest JSON AJAX CORS 四個名詞來開會 如何發請求 在前端的世界里也逛蕩了不少日子了,目前已經get到大約5種發起請求...
摘要:列表是編程中使用頻率極高的數據結構,由一系列按特定順序排列的元素組成,用表示,逗號分隔元素,類似中的數組。由于列表包含多個元素,所以通常命名為復數形式,如,等。使用切片裁剪獲取子列表使用列表名裁剪獲取對應索引區間的子列。 前言: 好久不見,突然發覺好久沒寫博客了,最近迷上了 Python 無法自拔,了解了一下,Python 簡單易學,尤其是接觸過 java 的人,入門 Python 更...
摘要:目錄如何用提高效率后端掘金經常有人說我應該學一門語言,比如之類,但是卻不知道如何入門。本文將通過我是如何開發公司年會抽獎系統的后端掘金需求出現年會將近,而年會抽獎環節必不可少,但是抽獎系統卻還沒有。 云盤一個個倒下怎么辦?無需編碼,手把手教你搭建至尊私享云盤 - 工具資源 - 掘金微盤掛了,360倒了,百度云盤也立了Flag。能讓我們在云端儲存分享文件的服務越來越少了。 買一堆移動硬盤...
閱讀 632·2021-11-22 15:32
閱讀 2720·2021-11-19 09:40
閱讀 2313·2021-11-17 09:33
閱讀 1263·2021-11-15 11:36
閱讀 1865·2021-10-11 10:59
閱讀 1475·2019-08-29 16:41
閱讀 1780·2019-08-29 13:45
閱讀 2150·2019-08-26 13:36