淺談Java設計模式——單例項、簡單工廠、抽象工廠、觀察者
最近的專案裡面涉及到一些Java設計模式,在此簡單談一下自己的看法,以下示例一部分參考同行,大部分自己設計。
1.單例模式
如果一個類始終只能建立一個例項,則這個類成為單例類,這種設計模式稱為單例模式。
class Singleton
{
// 使用一個類變數快取建立的例項
private static Singleton instance;
// 隱藏構造器
private Singleton(){
}
// 提供一個靜態方法,用於返回Singleton例項
public static Singleton getInstance ()
{
// 如果instance為null,表明還不曾建立Singleton物件
// 如果instance不為null,則表明已經建立了Singleton物件,將不會執行該方法
if (instance == null)
{
// 建立一個Singleton物件,並將其快取起來
instance = new Singleton();
}
//如果instance已經建立過,直接返回
return instance;
}
}
寫個測試程式
public class SingletonTest
{
public static void main(String[] args)
{
// 建立兩個Singleton 物件
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
// 將輸出true
System.out.println(s1 == s2);
}
}
Spring容器所管理的Bean例項預設是單例,可以通過scope來修改其行為方式。
使用單例模式的優勢:
(1)減少系統開銷(不用每次都建立新的例項)
(2)便於系統跟蹤單個例項的生命週期與例項狀態等。
2.簡單工廠
通過工廠來建立物件的設計模式稱為簡單工廠模式。
當需要建立一個物件的時候不通過new 來建立,而是通過向工廠下訂單來建立。
先定義一個房子(House),房子裡面有張桌子(Table),還可以獲得桌子的相關資訊(getTableMaterial()獲得桌子的材料)。
/**
*
* 一個House裡面有張桌子table(這個table是抽象的,需要工廠根據使用者的實際要求具體實現)
* table通過工廠tableFactory來生產
* tableFactory根據使用者所要求的材料material來定製table
*/
public class House {
private Table table;
public House(Table table){
this.table = table;
}
//獲得table 的material(所用材料)
public void getTableMaterial(){
table.getMaterial();
}
public static void main(String[] args){
TableFactory tableFactory = new TableFactory();
//通過工廠生產Table物件並傳遞給構造器
House house = new House(tableFactory.getTable("wood"));
house.getTableMaterial();
}
}
下面是Table介面,擁有Table的一般行為。
public interface Table {
public void getMaterial();
}
下面是三個具體的實現類(三種材質的桌子)。
/**
* 木桌
*/
public class WoodTable implements Table {
@Override
public void getMaterial() {
// TODO Auto-generated method stub
System.out.println("Material of this Table is Wood!");
}
}
/**
* 鐵桌
*/
public class SteelTable implements Table {
@Override
public void getMaterial() {
// TODO Auto-generated method stub
System.out.println("Material of this Table is Steel!");
}
}
/**
* 塑料桌
*/
public class PlasticTable implements Table {
@Override
public void getMaterial() {
// TODO Auto-generated method stub
System.out.println("Material of this Table is Plastic!");
}
}
下面是桌子工廠,可以根據使用者的需求生產相應的桌子。
public class TableFactory {
public Table getTable(String material){
switch(material){
case "wood":
return new WoodTable();
case "steel":
return new SteelTable();
default :
return new PlasticTable();
}
}
}
下面進行測試:
public static void main(String[] args){
TableFactory tableFactory = new TableFactory();
//工廠根據使用者的需求生產Table物件並傳遞給構造器
House house = new House(tableFactory.getTable("wood"));//使用者要求木桌(woodTable)
house.getTableMaterial();
}
測試結果:
使用者可以向工廠提出自己的需求(woodTable ,steelTable還是plasticTable),由工廠來生產Table(建立Table物件);
簡單工廠的優勢:物件的呼叫者與物件的建立過程分離,避免物件的呼叫與實現以硬編碼的方式耦合,提高系統的可維護性和可拓展性。
3.抽象工廠模式
抽象工廠簡單地說是工廠的工廠,抽象工廠可以建立具體工廠,由具體工廠來產生具體產品。
來看個示例。
有一個汽車商店(CarShop),裡面有一輛汽車(car),還可以獲得汽車的相關資訊。
public class CarShop {
private Car car;
public CarShop(Car car){
this.car = car;
}
//獲得汽車的相關資訊
public void getCarInfo(){
car.getCarInfo();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//通過抽象工廠獲得具體工廠
CarFactory cf = CarFactoryFactory.getCarFactory("A");
//通過具體工廠建立具體car物件並傳遞給構造器
CarShop cs = new CarShop(cf.createCar("BMW"));
cs.getCarInfo();
}
}
有一個抽象汽車工廠,用來根據使用者需求建立具體汽車工廠。
public class CarFactoryFactory {
//根據carType建立具體工廠
public static CarFactory getCarFactory(String carType){
switch(carType){
case "A":
return new ACarFactory();
case "B":
return new BCarFactory();
default :
return new CCarFactory();
}
}
}
有A、B、C三個具體汽車工廠,用來生產三種類型的汽車。
/**
* A工廠
* 根據使用者要求的品牌(carBrand)
* 生產A型別的汽車
*/
public class ACarFactory implements CarFactory {
@Override
public Car createCar(String carBrand) {
// TODO Auto-generated method stub
switch(carBrand){
case "BMW":
return new BMWCarA();
case "Benz":
return new BenzCarA();
default :
return new AudiCarA();
}
}
}
/**
* B工廠
* 根據使用者要求的品牌(carBrand)
* 生產B型別的汽車
*/
public class BCarFactory implements CarFactory {
/* (non-Javadoc)
* @see com.abstructFactory.CarFactory#createCar(java.lang.String)
*/
@Override
public Car createCar(String carBrand) {
switch(carBrand){
case "BMW":
return new BMWCarB();
case "Benz":
return new BenzCarB();
default :
return new AudiCarB();
}
}
}
/**
* C工廠
* 根據使用者要求的品牌(carBrand)
* 生產C型別的汽車
*/
public class CCarFactory implements CarFactory {
@Override
public Car createCar(String carBrand) {
switch(carBrand){
case "BMW":
return new BMWCarC();
case "Benz":
return new BenzCarC();
default :
return new AudiCarC();
}
}
}
這三個具體工廠類都實現了同一個汽車工廠介面,該介面擁有一般汽車工廠的行為。
public interface CarFactory {
public Car createCar(String carBrand);
}
A、B、C三個具體汽車工廠分別生產三種品牌(BMW,Benz和Audi)的汽車。
/**
* A工廠生產的Audi汽車
*/
public class AudiCarA implements Car {
@Override
public void getCarInfo() {
System.out.println("Audi car from ACarFactory");
}
}
/**
* A工廠生產的Benz汽車
*/
public class BenzCarA implements Car {
@Override
public void getCarInfo() {
System.out.println("Benz car from ACarFactory");
}
}
/**
* A工廠生產的BMW汽車
*/
public class BMWCarA implements Car {
@Override
public void getCarInfo() {
System.out.println("BMW car from ACarFactory");
}
}
/**
* B工廠生產的Audi汽車
*/
public class AudiCarB implements Car {
@Override
public void getCarInfo() {
System.out.println("Audi car from BCarFactory");
}
}
/**
* B工廠生產的Benz汽車
*/
public class BenzCarB implements Car {
@Override
public void getCarInfo() {
System.out.println("Benz car from BCarFactory");
}
}
/**
* B工廠生產的BMW汽車
*/
public class BMWCarB implements Car {
@Override
public void getCarInfo() {
System.out.println("BMW car from BCarFactory");
}
}
/**
* C工廠生產的Audi汽車
*/
public class AudiCarC implements Car {
@Override
public void getCarInfo() {
System.out.println("BMW car from CCarFactory");
}
}
/**
* C工廠生產的Benz汽車
*/
public class BenzCarC implements Car {
@Override
public void getCarInfo() {
System.out.println("BMW car from CCarFactory");
}
}
/**
* C工廠生產的BMW汽車
*/
public class BMWCarC implements Car {
@Override
public void getCarInfo() {
System.out.println("BMW car from CCarFactory");
}
}
這些具體的汽車產品都實現了同一個介面(Car),該介面擁有汽車的一般行為。
public interface Car {
public void getCarInfo();
}
下面來做個測試,使用者要求一輛A工廠生產的BMW汽車:
public static void main(String[] args) {
// TODO Auto-generated method stub
//通過抽象工廠獲得具體工廠
CarFactory cf = CarFactoryFactory.getCarFactory("A");
//通過具體工廠建立具體car物件並傳遞給構造器
CarShop cs = new CarShop(cf.createCar("BMW"));
cs.getCarInfo();
}
測試結果:
抽象工廠可以根據使用者的需求,通過不同的具體工廠生產不同的產品。
那麼抽象工廠模式有什麼好處呢?假如採用簡單工廠模式,假如只有A工廠,當用戶要求一輛B型別的BMW時,就要修改A工廠程式碼來生產BMWCarB物件。所以,抽象工廠模式的優勢是:物件的呼叫者與物件的實現類以及具體的工廠分離,程式碼的耦合性更低,系統可維護性以及可拓展性更高。
但是抽象工廠模式有個缺陷,就是當用戶需求改變的時候,需要修改程式碼,然後需要重新編譯,最好是將使用者需求(可以看做使用者訂單)放在一個配置檔案裡面,由程式碼根據配置檔案來建立相應的工廠以及例項,這樣當用戶需求發生改變的時候,只需要修改配置檔案(產品訂單)即可。
Spring IOC容器是一個抽象工廠,既可以管理Bean例項,還可以管理工廠例項。ApplicationContext.xml可以看作是系統的訂單,容器根據這個訂單生產相應的Bean。
4.觀察者模式
觀察者模式又稱釋出訂閱模式,定義了一種一對多依賴關係,讓一個或多個觀察者物件觀察一個主題物件,當主題物件的狀態發生改變時,系統會通知所有的依賴於此物件的觀察者,從而使得觀察者物件能夠自動更新。
看一個示例:
有一個任務列表TaskList,裡面的任務需要兩個程式設計師去完成,TaskList可以看作是被觀察的主題物件,兩個程式設計師可以看作是依賴於此主題物件的觀察者,當任務列表裡面的任務更新時,要通知這兩個程式設計師。
下面是TaskList:
public class TaskList extends Observable{
private List<String> allTask;
private static TaskList instance;
//私有構造器
private TaskList(){
}
public static TaskList getInstance(){
if(instance == null){
instance = new TaskList();
instance.allTask = new ArrayList<String>();
}
return instance;
}
//新增觀察者
public void addTaskObserver(Observer observer){
this.addObserver(observer);
}
//任務列表發生改變
public void addNewTask(String newTask){
allTask.add("new Task");
System.out.println("系統添加了新任務");
//被觀察物件發生改變
this.setChanged();
//通知觀察者並傳遞新任務
this.notifyObservers(newTask);
}
}
下面是兩個程式設計師:
public class ProgrammerA implements Observer{
//自動更新
@Override
public void update(Observable o, Object task) {
String newTask = (String)task;
System.out.println("ProgrammerA 您有新的任務: " +newTask);
}
}
public class ProgrammerB implements Observer{
//自動更新
@Override
public void update(Observable o, Object task) {
String newTask = (String)task;
System.out.println("ProgrammerB 您有新的任務: " +newTask);
}
}
下面做個測試:
public class ObserverTest {
public static void main(String[] args) {
//被觀察的任務列表
TaskList observable = TaskList.getInstance();
//觀察者
ProgrammerA pa = new ProgrammerA();
ProgrammerB pb = new ProgrammerB();
//新增觀察者
observable.addObserver(pa);
observable.addObserver(pb);
//任務列表發生改變
observable.addNewTask("new Task");
}
}
測試結果:
Java EE 應用中,主題/訂閱模式下的JMS就是觀察者模式的應用。
設計模式是對處於特定環境下,經常出現的某類軟體開發問題的一種相對成熟的設計方案,是軟體設計師集體經驗的體現。優雅的程式碼沒必要刻意使用哪種設計模式,最好是它本身就體現出設計模式。