1.單例模式
1.1 概念
簡單點說,就是一個應用程序中,某個類的實例對象只有一個,你沒有辦法去new仅乓,因為構(gòu)造器是被private修飾的,一般通過getInstance()的方法來獲取它們的實例蓬戚。
getInstance()的返回值是一個對象的引用夸楣,并不是一個新的實例,所以不要錯誤的理解成多個對象子漩。
步驟1.
創(chuàng)建一個單例對象:
public class SingleObject {
//創(chuàng)建 SingleObject 的一個對象
private static SingleObject instance = new SingleObject();
//讓構(gòu)造函數(shù)為 private豫喧,這樣該類就不會被實例化
private SingleObject(){}
//獲取唯一可用的對象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
步驟2.
從 singleton 類獲取唯一的對象。
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的構(gòu)造函數(shù)
//編譯時錯誤:構(gòu)造函數(shù) SingleObject() 是不可見的
//SingleObject object = new SingleObject();
//獲取唯一可用的對象
SingleObject object = SingleObject.getInstance();
//顯示消息
object.showMessage();
}
}
1.2 單例模式的幾種實現(xiàn)方式
1幢泼、懶漢式紧显,線程不安全
描述:這種方式是最基本的實現(xiàn)方式,這種實現(xiàn)最大的問題就是不支持多線程旭绒。因為沒有加鎖 synchronized,所以嚴格意義上它并不算單例模式。
這種方式 lazy loading 很明顯挥吵,不要求線程安全重父,在多線程不能正常工作。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2忽匈、懶漢式房午,線程安全
優(yōu)點:第一次調(diào)用才初始化,避免內(nèi)存浪費丹允。
缺點:必須加鎖 synchronized 才能保證單例郭厌,但加鎖會影響效率。
getInstance() 的性能對應用程序不是很關鍵(該方法使用不太頻繁)雕蔽。
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)存承冰。
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)域使用延遲初始化冈爹,應使用這種方式而不是雙檢鎖方式涌攻。這種方式只適用于靜態(tài)域的情況,雙檢鎖方式可在實例域需要延遲初始化時使用频伤。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6恳谎、枚舉
描述:這種實現(xiàn)方式還沒有被廣泛采用,但這是實現(xiàn)單例模式的最佳方法憋肖。它更簡潔因痛,自動支持序列化機制,絕對防止多次實例化岸更。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
2.觀察者模式
2.1 概念
對象間一對多的依賴關系鸵膏,當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新怎炊。
舉個栗子:假設有三個人谭企,小美(女廓译,22),小王和小李债查。小美很漂亮非区,小王和小李是兩個程序猿,時刻關注著小美的一舉一動盹廷。有一天征绸,小美說了一句:“誰來陪我打游戲啊《碚迹”這句話被小王和小李聽到了管怠,結(jié)果樂壞了,蹭蹭蹭缸榄,沒一會兒渤弛,小王就沖到小美家門口了,在這里碰凶,小美是被觀察者暮芭,小王和小李是觀察者,被觀察者發(fā)出一條信息欲低,然后觀察者們進行相應的處理辕宏,看代碼:
public interface Person {
//小王和小李通過這個接口可以接收到小美發(fā)過來的消息
void getMessage(String s);
}
這個接口相當于小王和小李的電話號碼,小美發(fā)送通知的時候就會撥打getMessage這個電話砾莱,撥打電話就是調(diào)用接口瑞筐,看不懂沒關系,先往下看
public class LaoWang implements Person {
private String name = "小王";
public LaoWang() {
}
@Override
public void getMessage(String s) {
System.out.println(name + "接到了小美打過來的電話腊瑟,電話內(nèi)容是:" + s);
}
}
public class LaoLi implements Person {
private String name = "小李";
public LaoLi() {
}
@Override
public void getMessage(String s) {
System.out.println(name + "接到了小美打過來的電話聚假,電話內(nèi)容是:->" + s);
}
}
代碼很簡單缭乘,我們再看看小美的代碼:
public class XiaoMei {
List<Person> list = new ArrayList<Person>();
public XiaoMei(){
}
public void addPerson(Person person){
list.add(person);
}
//遍歷list杰标,把自己的通知發(fā)送給所有暗戀自己的人
public void notifyPerson() {
for(Person person:list){
person.getMessage("你們過來吧,誰先過來誰就能陪我一起玩兒游戲!");
}
}
}
我們寫一個測試類來看一下結(jié)果對不對
public class Test {
public static void main(String[] args) {
XiaoMei xiao_mei = new XiaoMei();
LaoWang lao_wang = new LaoWang();
LaoLi lao_li = new LaoLi();
//小王和小李在小美那里都注冊了一下
xiao_mei.addPerson(lao_wang);
xiao_mei.addPerson(lao_li);
//小美向小王和小李發(fā)送通知
xiao_mei.notifyPerson();
}
}
3 裝飾者模式
對已有的業(yè)務邏輯進一步的封裝萝嘁,使其增加額外的功能财松,如Java中的IO流就使用了裝飾者模式瘪贱,用戶在使用的時候,可以任意組裝辆毡,達到自己想要的效果菜秦。 舉個栗子,我想吃三明治舶掖,首先我需要一根大大的香腸球昨,我喜歡吃奶油,在香腸上面加一點奶油眨攘,再放一點蔬菜主慰,最后再用兩片面包夾一下嚣州,很豐盛的一頓午飯,營養(yǎng)又健康共螺。(ps:不知道上海哪里有賣好吃的三明治的避诽,求推薦~)那我們應該怎么來寫代碼呢? 首先璃谨,我們需要寫一個Food類,讓其他所有食物都來繼承這個類鲤妥,看代碼:
public class Food {
private String food_name;
public Food() {
}
public Food(String food_name) {
this.food_name = food_name;
}
public String make() {
return food_name;
};
}
然后我們寫幾個子類繼承它:
//面包類
public class Bread extends Food {
private Food basic_food;
public Bread(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+面包";
}
}
//奶油類
public class Cream extends Food {
private Food basic_food;
public Cream(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+奶油";
}
}
//蔬菜類
public class Vegetable extends Food {
private Food basic_food;
public Vegetable(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+蔬菜";
}
}
Test類:
public class Test {
public static void main(String[] args) {
Food food = new Bread(new Vegetable(new Cream(new Food("香腸"))));
System.out.println(food.make());
}
}
4 適配器模式
將兩種完全不同的事物聯(lián)系到一起佳吞,就像現(xiàn)實生活中的變壓器。假設一個手機充電器需要的電壓是20V棉安,但是正常的電壓是220V底扳,這時候就需要一個變壓器,將220V的電壓轉(zhuǎn)換成20V的電壓贡耽,這樣衷模,變壓器就將20V的電壓和手機聯(lián)系起來了。
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
VoltageAdapter adapter = new VoltageAdapter();
phone.setAdapter(adapter);
phone.charge();
}
}
// 手機類
class Phone {
public static final int V = 220;// 正常電壓220v蒲赂,是一個常量
private VoltageAdapter adapter;
// 充電
public void charge() {
adapter.changeVoltage();
}
public void setAdapter(VoltageAdapter adapter) {
this.adapter = adapter;
}
}
// 變壓器
class VoltageAdapter {
// 改變電壓的功能
public void changeVoltage() {
System.out.println("正在充電...");
System.out.println("原始電壓:" + Phone.V + "V");
System.out.println("經(jīng)過變壓器轉(zhuǎn)換之后的電壓:" + (Phone.V - 200) + "V");
}
}
5 工廠模式
5.1 概念
工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一阱冶。這種類型的設計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式滥嘴。
在工廠模式中木蹬,我們在創(chuàng)建對象時不會對客戶端暴露創(chuàng)建邏輯,并且是通過使用一個共同的接口來指向新創(chuàng)建的對象若皱。
5.2 介紹
意圖:定義一個創(chuàng)建對象的接口镊叁,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創(chuàng)建過程延遲到子類進行走触。
主要解決:主要解決接口選擇的問題晦譬。
何時使用:我們明確地計劃不同條件下創(chuàng)建不同實例時。
如何解決:讓其子類實現(xiàn)工廠接口互广,返回的也是一個抽象的產(chǎn)品敛腌。
關鍵代碼:創(chuàng)建過程在其子類執(zhí)行。
應用實例: 1兜辞、您需要一輛汽車迎瞧,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的逸吵,以及這個汽車里面的具體實現(xiàn)凶硅。 2、Hibernate 換數(shù)據(jù)庫只需換方言和驅(qū)動就可以扫皱。
優(yōu)點: 1足绅、一個調(diào)用者想創(chuàng)建一個對象捷绑,只要知道其名稱就可以了。 2氢妈、擴展性高粹污,如果想增加一個產(chǎn)品,只要擴展一個工廠類就可以首量。 3壮吩、屏蔽產(chǎn)品的具體實現(xiàn),調(diào)用者只關心產(chǎn)品的接口加缘。
缺點:每次增加一個產(chǎn)品時鸭叙,都需要增加一個具體類和對象實現(xiàn)工廠,使得系統(tǒng)中類的個數(shù)成倍增加拣宏,在一定程度上增加了系統(tǒng)的復雜度沈贝,同時也增加了系統(tǒng)具體類的依賴。這并不是什么好事勋乾。
使用場景: 1宋下、日志記錄器:記錄可能記錄到本地硬盤、系統(tǒng)事件辑莫、遠程服務器等学歧,用戶可以選擇記錄日志到什么地方。 2各吨、數(shù)據(jù)庫訪問撩满,當用戶不知道最后系統(tǒng)采用哪一類數(shù)據(jù)庫,以及數(shù)據(jù)庫可能有變化時绅你。 3伺帘、設計一個連接服務器的框架,需要三個協(xié)議忌锯,"POP3"伪嫁、"IMAP"、"HTTP"偶垮,可以把這三個作為產(chǎn)品類张咳,共同實現(xiàn)一個接口。
5.3 代碼
步驟 1
創(chuàng)建一個接口:
public interface Shape {
void draw();
}
步驟 2
創(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.");
}
}
步驟 3
創(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;
}
}
步驟 4
使用該工廠砚哗,通過傳遞類型信息來獲取實體類的對象龙助。
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();
}
}
6 代理模式(proxy)
http://www.reibang.com/p/9bcac608c714
7 抽象工廠模式
7.1 概念
抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創(chuàng)建其他工廠军援。該超級工廠又稱為其他工廠的工廠。這種類型的設計模式屬于創(chuàng)建型模式称勋,它提供了一種創(chuàng)建對象的最佳方式胸哥。
在抽象工廠模式中,接口是負責創(chuàng)建一個相關對象的工廠赡鲜,不需要顯式指定它們的類空厌。每個生成的工廠都能按照工廠模式提供對象。
7.2 介紹
意圖:提供一個創(chuàng)建一系列相關或相互依賴對象的接口银酬,而無需指定它們具體的類蝇庭。
主要解決:主要解決接口選擇的問題。
何時使用:系統(tǒng)的產(chǎn)品有多于一個的產(chǎn)品族捡硅,而系統(tǒng)只消費其中某一族的產(chǎn)品。
如何解決:在一個產(chǎn)品族里面盗棵,定義多個產(chǎn)品壮韭。
關鍵代碼:在一個工廠里聚合多個同類產(chǎn)品。
應用實例:工作了纹因,為了參加一些聚會喷屋,肯定有兩套或多套衣服吧,比如說有商務裝(成套瞭恰,一系列具體產(chǎn)品)屯曹、時尚裝(成套,一系列具體產(chǎn)品)惊畏,甚至對于一個家庭來說恶耽,可能有商務女裝、商務男裝颜启、時尚女裝偷俭、時尚男裝,這些也都是成套的缰盏,即一系列具體產(chǎn)品涌萤。假設一種情況(現(xiàn)實中是不存在的,要不然口猜,沒法進入共產(chǎn)主義了负溪,但有利于說明抽象工廠模式),在您的家中济炎,某一個衣柜(具體工廠)只能存放某一種這樣的衣服(成套川抡,一系列具體產(chǎn)品),每次拿這種成套的衣服時也自然要從這個衣柜中取出了须尚。用 OOP 的思想去理解猖腕,所有的衣柜(具體工廠)都是衣柜類的(抽象工廠)某一個拆祈,而每一件成套的衣服又包括具體的上衣(某一具體產(chǎn)品),褲子(某一具體產(chǎn)品)倘感,這些具體的上衣其實也都是上衣(抽象產(chǎn)品)放坏,具體的褲子也都是褲子(另一個抽象產(chǎn)品)。
優(yōu)點:當一個產(chǎn)品族中的多個對象被設計成一起工作時老玛,它能保證客戶端始終只使用同一個產(chǎn)品族中的對象淤年。
缺點:產(chǎn)品族擴展非常困難,要增加一個系列的某一產(chǎn)品蜡豹,既要在抽象的 Creator 里加代碼麸粮,又要在具體的里面加代碼。
使用場景: 1镜廉、QQ 換皮膚弄诲,一整套一起換。 2娇唯、生成不同操作系統(tǒng)的程序齐遵。
注意事項:產(chǎn)品族難擴展,產(chǎn)品等級易擴展塔插。
7.3 代碼
步驟 1
為形狀創(chuàng)建一個接口梗摇。
public interface Shape {
void draw();
}
步驟 2
創(chuàng)建實現(xiàn)接口的實體類
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步驟 3
為顏色創(chuàng)建一個接口。
public interface Color {
void fill();
}
步驟4
創(chuàng)建實現(xiàn)接口的實體類想许。
class Red implements Color {
@Override
public void fill() {
System.out.println("Inside Red::fill() method.");
}
}
class Green implements Color {
@Override
public void fill() {
System.out.println("Inside Green::fill() method.");
}
}
class Blue implements Color {
@Override
public void fill() {
System.out.println("Inside Blue::fill() method.");
}
}
步驟 5
為 Color 和 Shape 對象創(chuàng)建抽象類來獲取工廠伶授。
public abstract class AbstractFactory {
public abstract Color getColor(String color);
public abstract Shape getShape(String shape) ;
}
步驟 6
創(chuàng)建擴展了 AbstractFactory 的工廠類,基于給定的信息生成實體類的對象流纹。
public class ShapeFactory extends AbstractFactory {
@Override
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;
}
@Override
public Color getColor(String color) {
return null;
}
}
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
return null;
}
@Override
public Color getColor(String color) {
if(color == null){
return null;
}
if(color.equalsIgnoreCase("RED")){
return new Red();
} else if(color.equalsIgnoreCase("GREEN")){
return new Green();
} else if(color.equalsIgnoreCase("BLUE")){
return new Blue();
}
return null;
}
}
步驟 7
創(chuàng)建一個工廠創(chuàng)造器/生成器類糜烹,通過傳遞形狀或顏色信息來獲取工廠。
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
步驟 8
使用 FactoryProducer 來獲取 AbstractFactory漱凝,通過傳遞類型信息來獲取實體類的對象景图。
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
步驟 8
使用 FactoryProducer 來獲取 AbstractFactory,通過傳遞類型信息來獲取實體類的對象碉哑。
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
//獲取形狀工廠
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
//獲取形狀為 Circle 的對象
Shape shape1 = shapeFactory.getShape("CIRCLE");
//調(diào)用 Circle 的 draw 方法
shape1.draw();
//獲取形狀為 Rectangle 的對象
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//調(diào)用 Rectangle 的 draw 方法
shape2.draw();
//獲取形狀為 Square 的對象
Shape shape3 = shapeFactory.getShape("SQUARE");
//調(diào)用 Square 的 draw 方法
shape3.draw();
//獲取顏色工廠
AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
//獲取顏色為 Red 的對象
Color color1 = colorFactory.getColor("RED");
//調(diào)用 Red 的 fill 方法
color1.fill();
//獲取顏色為 Green 的對象
Color color2 = colorFactory.getColor("Green");
//調(diào)用 Green 的 fill 方法
color2.fill();
//獲取顏色為 Blue 的對象
Color color3 = colorFactory.getColor("BLUE");
//調(diào)用 Blue 的 fill 方法
color3.fill();
}
}