單例模式
單例模式(Singleton Pattern)是java中最簡單的設計模式之一。這種類型的設計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。這種模式涉及到一個單一的類,該類負責創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一對象的方式,可以直接訪問,不需要實例化該類的對象。
注意:
(1)單例類只能有一個實例。
(2)單例類必須自己創(chuàng)建自己的唯一實例。
(3)單例類必須給所有其他對象提供這一實例。
優(yōu)點:
(1)在內(nèi)存里面只有一個實例,減少了內(nèi)存的開銷,尤其是平凡創(chuàng)建和銷毀實例。
(2)避免對文件的多重占用(比如寫文件操作)。
缺點:沒有接口,不能繼承,與單一職責原則沖突,一個類應該只關心內(nèi)部邏輯,而不關心外面怎么樣來實例化。
使用場景:
(1)要求生產(chǎn)唯一序列號。
(2)Web中的計數(shù)器,不用每次刷新都在數(shù)據(jù)庫里加一次,用單例先緩存起來。
(3)創(chuàng)建的一個對象需要消耗的資源過多,比如I/O與數(shù)據(jù)庫的鏈接等。
注意事項:getInstance()方法中需要使用同步鎖synchronized(Singleton.class)防止多線程同時進入造成instance被多次實例化。
實現(xiàn):
public class SingleObject {
//創(chuàng)建SingleOject的一個對象
private static SingleObject instance = new SingleObject();
//讓構造器函數(shù)為private,這樣類就不會被實例化
private SingleObject(){};
//獲取唯一可用的對象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("aaaa");
}
}
Spring中定義的bean默認為單例模式,保證每一個類中僅有一個實例,并提供一個訪問它的全局訪問點。spring中的單例模式提供了全局訪問點BeanFactory。但沒有從構造器級別去控制單例,這是因為spring管理的是任意的java對象。
單例模式的幾種實現(xiàn)方式
(1)懶漢式,線程不安全
這種方式是最基本的實現(xiàn)方式,這種實現(xiàn)的最大的問題就是不支持多線程。因為沒有加鎖synchronized,所以嚴格意義上不算單例模式。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
(2)懶漢式,線程安全
能夠在多線程中很好的工作,但是效率很低,99%的情況下不需要同步。
優(yōu)點:第一次調(diào)用才初始化,避免內(nèi)存浪費。
缺點:必須加synchronized才能保證單例,但加鎖會影響效率。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
(3)餓漢式
這種方法比較常用,但容易產(chǎn)生垃圾對象。
優(yōu)點:沒有加鎖,執(zhí)行效率會提高。
缺點:類加載時就初始化,浪費內(nèi)存。
它基于classloader機制避免了多線程的同步問題,不過,instance在類裝載時就實例化。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
(4)雙檢索/雙重校驗鎖(DCL,即double-checked locking)
這種方式采用雙鎖機制,安全且在多線程情況下保持高性能。
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)部類
這種方式能達到雙檢鎖方式一樣的功效,但實現(xiàn)更簡單。對靜態(tài)域使用延遲初始化,應該使用這種方式而不是雙檢鎖方式。
這種方式同樣使用了classLoader機制來保證初始化instace時只有一個線程,它跟第三種方式不同的是:第3種方式只要Singleton類被裝載了,那么instance就會被實例化,而這種方式是Singleton類被撞在了,instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有通過顯式調(diào)用getInstance方法時,才會顯式裝載Singleton類,從而實例化instance。
public class Singleton {
private static class SingletonHolder {
private static finale Singleton INSTANCE = new Singleton();
}
private Singleton (){};
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
(6)枚舉
這是實現(xiàn)單例模式的最佳方法。它更簡潔,自動支持序列化機制,防止被多次實例化。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
總結:一般情況下,不建議使用第一種和第二種懶漢方式,建議使用第三種餓漢方式,只有在明確要實現(xiàn)lazy loading 效果時,才會使用第五種登記方式。如果涉及到反序列化創(chuàng)建對象時,可以嘗試使用第六種枚舉方式。
工廠模式
工廠模式(Factory Pattern)是Java中最常用的設計模式之一。這種類型的設計模式屬于創(chuàng)建型模式。它提供了一種創(chuàng)建對象的最佳模式。在工廠模式中,我們在創(chuàng)建對象時不會對客戶端暴露創(chuàng)建邏輯,并且通過使用一個共同的接口來指向新創(chuàng)建的對象。
優(yōu)點:
(1)一個調(diào)用者想創(chuàng)建一個對象,只要知道其名稱就可以了。
(2)擴展性高,如果想要增加一個產(chǎn)品,只要擴展一個工廠類就可以了。
(3)屏蔽產(chǎn)品的具體實現(xiàn),調(diào)用者只關心產(chǎn)品的接口。
注意事項:作為一種創(chuàng)建類模式,在任何需要生成復雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是復雜對象適合使用工廠模式,而簡單對象,特別是只需要通過new就可以完成創(chuàng)建的對象,無需使用工廠模式。如果使用工程模式,就需要引入一個工廠類,會增加系統(tǒng)的復雜度。
使用場景:
(1)日志記錄器:記錄可以記錄到本地磁盤、系統(tǒng)事件、遠程服務器等,用戶可以選擇日志記錄到什么地方。
(2)數(shù)據(jù)庫訪問,當用戶不知道最后系統(tǒng)采用哪一類數(shù)據(jù)庫時,以及數(shù)據(jù)庫可能有變化時。
(3)設計一個連接服務器的框架,需要三個協(xié)議,“POP3”,“IMAP”,“HTTP”,可以把三個作為產(chǎn)品類,實現(xiàn)同一個接口。
Spring中使用工廠模式通過BeanFactory、ApplicationContext創(chuàng)建Bean對象。
實現(xiàn)方式:
我們創(chuàng)建一個Shape接口和實現(xiàn)Shape接口的實體類。下一步是定義工廠類ShapeFactory。
FactoryPatternDemo類使用ShapeFactory來獲取Shape對象。它將向ShapeFactory傳遞信息(CIRCLE / RECTANGLE / SQUARE),以便獲取它所需對象的類型。
步驟一:創(chuàng)建一個接口
public insterface Shape{
void draw();
}
步驟二:創(chuàng)建實現(xiàn)接口的實體類
public class Rectangle implements Shape{
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步驟三:創(chuàng)建一個工廠,生成基于給定信息的實體類的對象
public class ShapeFactory{
//使用getShape方法獲取形狀類型的對象
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
步驟四:使用該工廠,通過傳遞
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//獲取 Circle 的對象,并調(diào)用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//調(diào)用 Circle 的 draw 方法
shape1.draw();
//獲取 Rectangle 的對象,并調(diào)用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//調(diào)用 Rectangle 的 draw 方法
shape2.draw();
//獲取 Square 的對象,并調(diào)用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//調(diào)用 Square 的 draw 方法
shape3.draw();
}
}
抽象工廠模式
抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創(chuàng)建其他工廠,該超級工廠又稱為其他工廠的工廠。這種類型的設計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
在抽象工廠模式中,接口是負責創(chuàng)建一個相關對象的工廠,不需要顯式地指定它們的類,每個生成的工廠都能按照工廠模式提供對象。
優(yōu)點:當一個產(chǎn)品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產(chǎn)品族中的對象。
缺點:產(chǎn)品族擴展非常困難,要增加一個系列的某一產(chǎn)品,既要在抽象的Creator里加代碼,又要在具體的里面加代碼。
使用場景:
(1)QQ換皮膚,一整套一起換
(2)生成不同操作系統(tǒng)的程序
注意事項:
產(chǎn)品族難擴展, 產(chǎn)品等級易擴展
策略模式
在策略模式中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬于行為型模式。
在策略模式中,我們創(chuàng)建表示各種策略的對象和一個行為隨著策略對象改變而改變的context對象。策略對象改變context對象的執(zhí)行算法。
意圖:定義一系列的算法,把他們一個個封裝起來,并且使他們可相互替換。
主要解決:在多種算法相似的情況下,使用if…else所帶來的復雜和難以維護。
何時使用:一個系統(tǒng)有許多類,而區(qū)分它們的只是他們直接的行為。
如何解決:將這些算法封裝成一個一個的類,任意的替換。
優(yōu)點:
(1)算法可以自由切換。
(2)避免使用多重條件判斷。
(3)擴展性良好。
缺點:
(1)策略類會增多。
(2)所有策略類都需要對外暴露。
Spring中資源訪問接口Resource的設計是一種經(jīng)典的策略模式。Resource接口是所有資源訪問類所實現(xiàn)的接口,Resource接口就代表資源訪問策略,但具體采用哪種策略實現(xiàn),Resource接口并不理會??蛻舳顺绦蛑缓蚏esource接口耦合,并不知道底層采用何種資源訪問策略,這樣客戶端可以再不同的資源訪問策略之間自由切換。
實現(xiàn)方法:
創(chuàng)建一個定義活動的Strategy接口和實現(xiàn)了Strategy
步驟一:
創(chuàng)建一個接口:
public interface Strategy {
public int doOperation(int num1, int num2);
}
步驟二
創(chuàng)建接口的實現(xiàn)類
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubtract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
步驟三
創(chuàng)建Context類
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);
}
}
步驟四
使用Context來 查看當它改變策略Strategy時的行為變化
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 OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
代理模式
在代理模式中,一個類代表另一個類的功能。這種類型的設計模式屬于結構型模式。在代理模式中,我們創(chuàng)建具有現(xiàn)有對象的對象,以便向外界提供功能接口。文章來源:http://www.zghlxwxcb.cn/news/detail-601073.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-601073.html
到了這里,關于java Spring中使用到的設計模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!