設計模式及案例
23種設計模式概要及易懂的例子
23種設計模式(分為三大類)
設計模式(Design Pattern)是前輩們對程式碼開發經驗的總結,是解決特定問題的一系列套路。它不是語法規定,而是一套用來提高程式碼可複用性、可維護性、可讀性、穩健性以及安全性的解決方案。
為什麼要使用設計模式?
為了程式碼複用,增加可維護性
設計模式的六大原則
1、開閉原則(Open Close Principle)
開閉原則的意思是:對擴充套件開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。簡言之,是為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類,後面的具體設計中我們會提到這點。
2、里氏代換原則(Liskov Substitution Principle)
里氏代換原則是面向物件設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟體單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。
3、依賴倒轉原則(Dependence Inversion Principle)
這個原則是開閉原則的基礎,具體內容:針對對介面程式設計,依賴於抽象而不依賴於具體。
4、介面隔離原則(Interface Segregation Principle)
這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟體架構出發、便於升級和維護的軟體設計思想,它強調降低依賴,降低耦合。
5、迪米特法則,又稱最少知道原則(Demeter Principle)
最少知道原則是指:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模組相對獨立。
6、合成複用原則(Composite Reuse Principle)
合成複用原則是指:儘量使用合成/聚合的方式,而不是使用繼承。
建立型模式(共五種)
單例模式
概念
保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。
個人總結:在類內部創造單一物件,通過設定構造方法許可權,使類外部無法再創造物件
注:
1、單例類只能有一個例項。
2、單例類必須自己建立自己的唯一例項。
3、單例類必須給所有其他物件提供這一例項
建立方式
主要使用懶漢和懶漢式
1、餓漢式:類初始化時,會立即載入該物件,執行緒天生安全,呼叫效率高。
2、懶漢式: 類初始化時,不會初始化該物件,真正需要使用的時候才會建立該物件,具備懶載入功能。
3、靜態內部方式:結合了懶漢式和餓漢式各自的優點,真正需要物件的時候才會載入,載入類是執行緒安全的。
4、列舉單例: 使用列舉實現單例模式 優點:實現簡單、呼叫效率高,列舉本身就是單例,由jvm
從根本上提供保障!避免通過反射和反序列化的漏洞, 缺點沒有延遲載入。
5、雙重檢測鎖方式 (因為JVM
本質重排序的原因,可能會初始化多次,不推薦使用)
例項解析
1.餓漢式
該模式的特點是類一旦載入就建立一個單例,保證在呼叫getInstance
方法之前單例已經存在了。餓漢式單例在類建立的同時就已經建立好一個靜態的物件供系統使用,以後不再改變,所以是執行緒安全的,可以直接用於多執行緒而不會出現問題。
public class HungrySingleton {
//類初始化時,會立即載入該物件,執行緒安全,呼叫效率高
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
2.懶漢式
該模式的特點是類載入時沒有生成單例,只有當第一次呼叫getlnstance
方法時才去建立這個單例。
public class LazySingleton {
private LazySingleton() { }//1.private 避免類在外部被例項化
private static volatile LazySingleton instance = null;//2.保證instance在所有執行緒中同步
public static LazySingleton getInstance() {//3
if (instance == null) {//4第一次檢查
synchronized(LazySingleton.class){//5加鎖
if(instance == null){//6第二次檢查
instance = new LazySingleton();
}
}
}
return instance;
}
}
懶漢式和餓漢式區別
1、執行緒安全
餓漢式天生就是執行緒安全的,可以直接用於多執行緒。
懶漢式本身就非執行緒安全的,但是可以通過關鍵字volatile
和 synchronized
來實現執行緒安全。
2、資源載入和效能
餓漢式在類建立的同時就例項化一個靜態物件出來,不管之後會不會使用這個單例,都會佔據一定的記憶體,但是相應的,在第一次呼叫時速度也會更快,因為其資源已經初始化完成。
而懶漢式顧名思義,會延遲載入,在第一次使用該單例的時候才會例項化物件出來,第一次呼叫時要做初始化,如果要做的工作比較多,效能上會有些延遲,之後就和餓漢式一樣了。
優缺點
優點:
1、在記憶體裡只有一個例項,減少了記憶體的開銷,尤其是頻繁的建立和銷燬例項。
2、避免對資源的多重佔用(比如寫檔案操作)。
缺點:
沒有介面,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來例項化。
使用場景
1、要求生產唯一序列號。
2、WEB
中的計數器,不用每次重新整理都在資料庫里加一次,用單例先快取起來。
3、建立的一個物件需要消耗的資源過多,比如I/O
與資料庫的連線等。注意事項:getInstance()
方法中需要使用同步鎖,synchronized (Singleton.class)
防止多執行緒同時進入造成instance
被多次例項化。
注意:如果編寫的是多執行緒程式,則不要刪除上例程式碼中的關鍵字volatile
和 synchronized
,否則將存線上程非安全的問題。如果不刪除這兩個關鍵字就能保證執行緒安全,但是每次訪問時都要同步,會影響效能,且消耗更多的資源,這是懶漢式單例的缺點。單例模式中volatile關鍵字的作用
拓展
單例模式可擴充套件為有限的多例(Multitcm)模式,這種模式可生成有限個例項並儲存在ArrayList
中,客戶需要時可隨機獲取
工廠模式
概念
它提供了一種建立物件的最佳方式。在工廠模式中,我們在建立物件時不會對客戶端暴露建立邏輯,並且是通過使用一個共同的介面來指向新建立的物件。實現了建立者和呼叫者分離,工廠模式分為簡單工廠、工廠方法、抽象工廠模式。
好處
1、工廠模式是我們最常用的例項化物件模式了,是用工廠方法代替new操作的一種模式。
2、利用工廠模式可以降低程式的耦合性,為後期的維護修改提供了很大的便利。
3、將選擇實現類、建立物件統一管理和控制。從而將呼叫者跟我們的實現類解耦。
Spring開發中的工廠設計模式
1.Spring IOC
- 看過Spring原始碼就知道,在Spring IOC容器建立bean的過程是使用了工廠設計模式
- Spring中無論是通過xml配置還是通過配置類還是註解進行建立bean,大部分都是通過簡單工廠來進行建立的。
- 當容器拿到了beanName和class型別後,動態的通過反射建立具體的某個物件,最後將建立的物件放到Map中。
2.為什麼Spring IOC要使用工廠設計模式建立Bean呢
- 在實際開發中,如果我們A物件呼叫B,B呼叫C,C呼叫D的話我們程式的耦合性就會變高。(耦合大致分為類與類之間的依賴,方法與方法之間的依賴。)
- 在很久以前的三層架構程式設計時,都是控制層呼叫業務層,業務層呼叫資料訪問層時,都是是直接new物件,耦合性大大提升,程式碼重複量很高,物件滿天飛
- 為了避免這種情況,Spring使用工廠模式程式設計,寫一個工廠,由工廠建立Bean,以後我們如果要物件就直接管工廠要就可以,剩下的事情不歸我們管了。Spring IOC容器的工廠中有個靜態的Map集合,是為了讓工廠符合單例設計模式,即每個物件只生產一次,生產出物件後就存入到Map集合中,保證了例項不會重複影響程式效率。
工廠模式分類
工廠模式分為簡單工廠、工廠方法、抽象工廠模式
簡單工廠 :用來生產同一等級結構中的任意產品。(不支援拓展增加產品)
工廠方法 :用來生產同一等級結構中的固定產品。(支援拓展增加產品)
抽象工廠 :用來生產不同產品族的全部產品。(不支援拓展增加產品;支援增加產品族)
簡單工廠模式
簡單工廠模式相當於是一個工廠中有各種產品,建立在一個類中,客戶無需知道具體產品的名稱,只需要知道產品類所對應的引數即可。但是工廠的職責過重,而且當型別過多時不利於系統的擴充套件維護。
public class Simple_Factory {
public static void main(String[] args){
Car aodi = CarFactory.createCar("aodi");
Car bmw = CarFactory.createCar("bmw");
aodi.run();
bmw.run();
}
}
interface Car { //汽車
void run();
}
class Bmw implements Car {
@Override
public void run() {
System.out.println("this is bmw");
}
}
class AoDi implements Car {
public void run() {
System.out.println("this is aodi");
}
}
class CarFactory {
public static Car createCar(String name) {
if(name.equalsIgnoreCase("aodi")){
return new AoDi();
}
if(name.equalsIgnoreCase("bmw")){
return new Bmw();
}
return null;
}
}
- 優點/缺點
優點:簡單工廠模式能夠根據外界給定的資訊,決定究竟應該建立哪個具體類的物件。明確區分了各自的職責和權力,有利於整個軟體體系結構的優化。
缺點:很明顯工廠類集中了所有例項的建立邏輯,容易違反GRASPR的高內聚的責任分配原則
工廠方法模式
工廠方法模式Factory Method,又稱多型性工廠模式。在工廠方法模式中,核心的工廠類不再負責所有的產品的建立,而是將具體建立的工作交給子類去做。該核心類成為一個抽象工廠角色,僅負責給出具體工廠子類必須實現的介面,而不接觸哪一個產品類應當被例項化這種細節。
public class Factory_Method {
public static void main(String[] args) {
Car aodi = new AoDiFactory().createCar();
Car bmw = new BmwFactory().createCar();
aodi.run();
bmw.run();
}
}
//建立工廠
interface Car {
void run();
}
//建立工廠方法呼叫介面(所有的產品需要new出來必須繼承他來實現方法)
interface CarFactory {
Car createCar();
}
//建立工廠的產品
class AoDi implements Car {
@Override
public void run() {
System.out.println("this is aodi");
}
}
class Bmw implements Car {
@Override
public void run() {
System.out.println("this is bmw");
}
}
//建立工廠方法呼叫介面的例項
class AoDiFactory implements CarFactory {
public Car createCar() {
return new AoDi();
}
}
class BmwFactory implements CarFactory {
public Car createCar() {
return new Bmw();
}
}
抽象工廠模式
抽象工廠簡單地說是工廠的工廠,抽象工廠可以建立具體工廠,由具體工廠來產生具體產品。
1.建立第一個子工廠
//汽車
interface Car {
void run();
}
class CarA implements Car{
public void run() {
System.out.println("寶馬");
}
}
class CarB implements Car{
public void run() {
System.out.println("摩拜");
}
}
2.建立第二個子工廠
//發動機
interface Engine {
void run();
}
class EngineA implements Engine {
public void run() {
System.out.println("轉的快!");
}
}
class EngineB implements Engine {
public void run() {
System.out.println("轉的慢!");
}
}
//3.建立一個總工廠
interface TotalFactory {
// 建立汽車
Car createChair();
// 建立發動機
Engine createEngine();
}
//總工廠實現類,由他決定呼叫哪個工廠的那個例項
class TotalFactoryReally implements TotalFactory {
public Engine createEngine() {
return new EngineA();
}
public Car createChair() {
return new CarA();
}
}
4.測試
public class Abstract_Factory {
public static void main(String[] args) {
TotalFactory totalFactory2 = new TotalFactoryReally();
Car car = totalFactory2.createChair();
car.run();
TotalFactory totalFactory = new TotalFactoryReally();
Engine engine = totalFactory.createEngine();
engine.run();
}
}
建造者模式
將一個複雜的構建與其表示相分離,使得同樣的構建過程可以建立不同的表示。
個人總結:將一些不會變的基本元件,通過builder
,組合,構建複雜物件,實現分離
public class BuilderDemo {
public static void main(String[] args) {
PriceBuilder priceBuilder = new PriceBuilder();
System.out.println("Car1和Car2:"+priceBuilder.Car1AndCar2());
System.out.println("Car1和Bus:"+priceBuilder.Car1AndBus());
}
}
//基本元件
interface Car {
}
//基本元件1
class Car1 implements Car{
int price = 20;
}
//基本元件2
class Car2 implements Car{
int price = 90;
}
//基本元件3
class Bus {
int price = 500;
}
class PriceBuilder {
//car1和car2的總價格
public int Car1AndCar2() {
int priceOfCar1 = new Car1().price;
int priceOfCar2 = new Car2().price;
return priceOfCar1+priceOfCar2;
}
//car1和bus的總價格
public int Car1AndBus() {
int priceOfCar1 = new Car1().price;
int priceOfBus = new Bus().price;
return priceOfCar1+priceOfBus;
}
}
原型模式
用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。
個人總結:將物件複製了一份並返還給呼叫者,物件需繼承Cloneable並重寫clone()方法
public class Prototype implements Cloneable{
private String message = "hello";
public Object clone() throws CloneNotSupportedException{
Prototype proto = (Prototype) super.clone();
//操作克隆物件
proto.message += " world!";
return proto;
}
public static void main(String[] args) throws CloneNotSupportedException {
Prototype p = (Prototype)new Prototype().clone();
//操作克隆物件
System.out.println(p.message);
}
}
結構型模式(共七種)
介面卡模式
將一個類的介面轉換成客戶希望的另外一個介面。介面卡模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
個人總結:銜接兩個不相容、獨立的介面的功能,使得它們能夠一起工作。介面卡起中介作用。
public class Adapter {
public static void main(String[] args) {
//相容了高階功能的普通播放器
Player player = new Player();
player.play();
}
}
//普通的播放器
interface MediaPlayer {
public void play();
}
//高階的播放器
interface AdvanceMediaPlayer {
public void playVideo();
}
//視訊播放器(高階的播放器)
class VideoPlayer implements AdvanceMediaPlayer {
@Override
public void playVideo(){
System.out.println("play video!");
}
}
//介面卡(銜接了普通播放器與高階播放器這兩個獨立介面的功能)
class MediaAdapter implements MediaPlayer {
AdvanceMediaPlayer advanceMediaPlayer;
public MediaAdapter() {
advanceMediaPlayer = new VideoPlayer();
}
@Override
public void play() {
advanceMediaPlayer.playVideo();
}
}
//普通播放器
class Player implements MediaPlayer {
//相容高階播放器的介面卡
MediaAdapter mediaAdapter = new MediaAdapter();
@Override
public void play() {
mediaAdapter.play();
}
}
裝飾器模式
動態地給一個物件新增一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活。
個人總結:建立類的裝飾類,對被裝飾類增強功能。裝飾模式是繼承的一個替代模式。
public class Decorator {
public static void main(String[] args) {
Animals dog = new AnimalsDecorator(new Dog());
dog.run();
}
}
interface Animals {
public void run();
}
//被裝飾類
class Dog implements Animals{
@Override
public void run() {
System.out.println("dog run!");
}
}
//裝飾類
class AnimalsDecorator implements Animals {
private Animals animals;
//動態裝飾,引數為Animals介面,傳入什麼實現就裝飾什麼實現
//繼承不能做到這一點,繼承的功能是靜態的,不能動態增刪。
public AnimalsDecorator(Animals animals) {
this.animals = animals;
}
@Override
//裝飾run()方法
public void run() {
animals.run();
System.out.println("fast!");
}
}
代理模式
為其他物件提供一種代理以控制對這個物件的訪問。
個人總結:建立類的代理類,間接訪問被代理類的過程中對其功能加以控制,例如在某個函式執行前後新增額外功能。(代理例子:買火車票不一定在火車站買,也可以去代售點)。和裝飾器模式的區別:裝飾器模式為了增強功能,而代理模式是為了加以控制,"形式"雖然相似,"語義"卻截然不同"。起中介作用
public class Proxy {
public static void main(String[] args) {
Animals dog = new DogProxy(new Dog());
dog.run();
}
}
interface Animals {
public void run();
}
class Dog implements Animals {
@Override
public void run() {
System.out.println("run!");
}
}
//通過代理類,在被代理類的run()方法執行前後新增額外的功能
class DogProxy implements Animals {
private Animals animals;
public DogProxy(Animals animals){
super();
this.animals = animals;
}
@Override
public void run() {
before();
animals.run();
atfer();
}
private void atfer() {
System.out.println("after run!");
}
private void before() {
System.out.println("before run!");
}
}
外觀模式
為子系統中的一組介面提供一個一致的介面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。
個人總結:在客戶端和複雜系統之間再加一層,在這一層中將呼叫順序、依賴關係等處理好。提供一個容易使用的外觀層。
public class Facade {
public static void main(String[] args) {
Computer computer = new Computer();
computer.put();
}
}
class CPU {
public void work(){
//複雜的操作
System.out.println("CPU is working!");
}
}
class Disk {
public void put(){
//複雜的操作
System.out.println("put in disk!");
}
}
//外觀類,隱藏了系統的複雜性,提供簡化的方法(訪問系統的介面)
//客戶端不需要知道系統內部的複雜聯絡
class Computer {
private CPU cpu;
private Disk disk;
public Computer(){
cpu = new CPU();
disk = new Disk();
}
public void work(){
cpu.work();
}
public void put(){
disk.put();
}
}
橋接模式
將抽象部分與實現部分分離,使它們都可以獨立的變化。
個人總結:通過對Bridge類的呼叫,實現了對同一介面下不同實現類的呼叫;建立一個繼承於同一抽象的不同實現類之間的關聯關係,這個關係由Bridge類橋接起來。
public class Bridge {
public static void main(String[] args) {
AnimalsBridge bridge = new AnimalsBridge(new Dog());
bridge.method();
}
}
//介面
interface Animals {
public void method();
}
//實現1
class Cat implements Animals {
@Override
public void method() {
System.out.println("this is cat!");
}
}
//實現2
class Dog implements Animals {
@Override
public void method() {
System.out.println("this is dog!");
}
}
//將Animals介面下的不同實現,
//通過橋接模式使它們在抽象層建立一個關聯關係。
//實現之間獨立變化,減少耦合
class AnimalsBridge {
private Animals animals;
public AnimalsBridge(Animals animals) {
this.animals = animals;
}
public void method(){
animals.method();
}
}
組合模式
將物件組合成樹形結構以表示"部分-整體"的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性。
個人總結:建立了一個包含自己物件組的類,並提供修改物件組的方法。應用場景,如樹形選單,檔案、資料夾的管理。
public class Composite {
public static void main(String[] args) {
Person person = new Person("小明");
person.addFriends(new Person("小紅"));
person.addFriends(new Person("小白"));
System.out.println(person.getFriends());
}
}
class Person {
private String name;
//包含自己的物件組
private List<Person> friends = new ArrayList<Person>();
public Person(String name){
this.name = name;
}
public Person addFriends(Person p){
friends.add(p);
return this;
}
public String getName(){
return this.name;
}
public List<Person> getFriends(){
return this.friends;
}
public String toString(){
return this.name;
}
}
享元模式
運用共享技術有效地支援大量細粒度的物件。
個人總結:重用現有的同類物件,若未找到匹配的物件,則建立新物件。例如,資料庫的連線池。減少物件的建立,降低系統記憶體,提高效率。
public class Flyweight {
public static void main(String[] args) {
//red Circle預設存在,所以拿的時候不用new
Circle circle = CircleFactory.getCircle("red");
circle.draw();
for(int i=0;i<2;i++) {
//第一次拿的時候需要new green Circle,第二次拿的時候不用new
circle = CircleFactory.getCircle("green");
circle.draw();
}
}
}
class Circle {
private String color;
public Circle(String color){
this.color = color;
}
public void draw(){
System.out.println(color+" Circle!");
}
}
class CircleFactory {
private static final HashMap<String, Circle> circleMap = new HashMap<String, Circle>();
static {
//初始化,存放red Circle
circleMap.put("red", new Circle("red"));
}
public static Circle getCircle(String color) {
Circle circle = (Circle)circleMap.get(color);
//Map如果不存在該顏色的Circle,則新建
if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("new a circle of color: "+color);
}
//如果存在,則返回Map中的物件
return circle;
}
}
行為型模式(共十一種)
策略模式
定義一系列的演算法,把它們一個個封裝起來, 並且使它們可相互替換。
個人總結:統一介面下的一系列演算法類(多種策略),用一個類將其封裝起來,使它們(多種策略)可動態切換。和工廠模式的區別:工廠模式是建立型模式,是為了建立不同物件;而策略模式是行為模式,為了選擇不同的行為。
public class Strategy {
public static void main(String[] args) {
OperationStrategy operationStrategy = new OperationStrategy(new OperationAdd());
operationStrategy.executeStrategy(15, 21);
}
}
interface Operation{
public void doOperation(int a, int b);
}
//策略1
class OperationAdd implements Operation{
public void doOperation(int a, int b){
System.out.println(a+"+"+b+"="+(a+b));
}
}
//策略2
class OperationMultiply implements Operation{
public void doOperation(int a, int b){
System.out.println(a+"*"+b+"="+(a*b));
}
}
//封裝一系列策略,可任意替換策略(實現同一個介面)
class OperationStrategy{
private Operation operation;
public OperationStrategy(Operation operation){
this.operation = operation;
}
//執行策略
public void executeStrategy(int a, int b){
operation.doOperation(a, b);
}
}
模板方法模式
定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
個人總結:將一些固定步驟、固定邏輯的方法封裝成模板方法。呼叫模板方法即可完成那些特定的步驟。例如,spring中對Hibernate的事務管理,開啟session、關閉session等固定步驟不需重複寫,直接丟給一個實體儲存。
public class Template {
public static void main(String[] args) {
Game game = new FootballGame();
game.play();
}
}
abstract class Game {
//步驟1,初始化遊戲
abstract void initialize();
//步驟2,開始遊戲
abstract void startPlay();
//,步驟3,結束遊戲
abstract void endPlay();
//主方法,模板方法,設定為final,在抽象類中實現
public final void play() {
initialize();
startPlay();
endPlay();
}
}
class FootballGame extends Game {
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
}
觀察者模式
定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。
個人總結:一個物件(被觀察者)狀態變化時,通知所有依賴於它的物件(觀察者);這種依賴方式具有雙向性:觀察者指定被觀察的物件,或者被觀察物件新增觀察者,下面例子採用後者方式
public class Observer {
public static void main(String[] args) {
Subject subject = new Subject();
subject.addSubjectObserver(new Observer1());
subject.addSubjectObserver(new Observer2());
subject.setState(1);
}
}
class Subject {
//一對多關係,多個該類的觀察者
private List<SubjectObserver> subjectObservers = new ArrayList<SubjectObserver>();
//狀態(被觀察),發生變化時通知所有觀察者
private int state;
public void setState(int state) {
this.state = state;
//改變狀態,通知所有觀察者
notifyAllSubjectObservers();
}
public void addSubjectObserver(SubjectObserver subjectObserver) {
subjectObservers.add(subjectObserver);
}
//通知所有觀察者
public void notifyAllSubjectObservers() {
for (SubjectObserver subjectObserver : subjectObservers) {
subjectObserver.alert();
}
}
}
abstract class SubjectObserver {
protected Subject subject;
public abstract void alert();
}
//觀察者1
class Observer1 extends SubjectObserver {
@Override
public void alert() {
System.out.println("Observer1: subject is changed!");
}
}
//觀察者2
class Observer2 extends SubjectObserver {
@Override
public void alert() {
System.out.println("Observer2: subject is changed!");
}
}
迭代器模式
提供一種方法順序訪問一個聚合物件中各個元素, 而又無須暴露該物件的內部表示。
個人總結:Java中的iterator的簡單實現原理。將聚合類中遍歷各個元素的行為分離出來,封裝成迭代器,讓迭代器來處理遍歷的任務;使簡化聚合類,同時又不暴露聚合類的內部。
public class IteratorDemo {
public static void main(String[] args) {
MyContainer myContainer = new MyContainer();
Iterator iterator = myContainer.getIterator();
while(iterator.hashNext())
System.out.println(iterator.next());
}
}
//迭代器介面
interface Iterator {
public boolean hashNext();
public Object next();
}
//容器介面
interface Container {
public Iterator getIterator();
}
//自定義容器(聚合類)
class MyContainer implements Container{
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
@Override
public Iterator getIterator(){
return new MyIterator();
}
//自定義迭代器,迭代器類定義為容器類的內部類
private class MyIterator implements Iterator{
int index = 0;
//自定義遍歷規則
@Override
public boolean hashNext(){
if(index < names.length)
return true;
return false;
}
@Override
public Object next(){
if(this.hashNext())
return names[index++];
return null;
}
}
}
責任鏈模式
避免請求傳送者與接收者耦合在一起,讓多個物件都有可能接收請求,將這些物件連線成一條鏈,並且沿著這條鏈傳遞請求,直到有物件處理它為止。
個人總結:在Handler類裡面聚合自己,形成一條Handler鏈(或樹、環等),並且可以將請求往下一個Handler傳遞(只允許傳給另一個,而不允許傳給多個)。例子:Struts攔截器,Filter過濾器
public class Chain_of_Responsibility {
public static void main(String[] args) {
ResponsibilityHandler handler1 = new ResponsibilityHandler("handler1");
ResponsibilityHandler handler2 = new ResponsibilityHandler("handler2");
ResponsibilityHandler handler3 = new ResponsibilityHandler("handler3");
handler1.setResponsibilityHandler(handler2);
handler2.setResponsibilityHandler(handler3);
handler1.operator();//操作請求會沿著這條鏈傳遞下去,
}
}
//責任處理器/接收器
class ResponsibilityHandler {
//聚合自己,構成一條責任鏈
private ResponsibilityHandler responsibilityHandler = null;
private String name;
public ResponsibilityHandler(String name){
this.name = name;
}
public ResponsibilityHandler next(){
return this.responsibilityHandler;
}
public void setResponsibilityHandler(ResponsibilityHandler responsibilityHandler){
this.responsibilityHandler = responsibilityHandler;
}
public void operator(){
System.out.println(name+" is handler!");
if(this.next() != null){
//將請求傳送到下一個責任接收器
next().operator();
}
}
}
命令模式
將一個請求封裝成一個物件,從而使您可以用不同的請求對客戶進行引數化。
個人總結:三種角色(呼叫者→接受者→命令);解耦行為請求者和行為實現著,實現請求和執行分開;呼叫者選擇命令釋出,命令指定執行者。
public class CommandDemo {
public static void main(String[] args) {
Receiver receiver = new Receiver("小明");
//指定命令的執行者
Command shootCommand = new ShootCommand(receiver);
Command otherCOmmand = new OtherCommand(receiver);
Invoker invoker = new Invoker();
invoker.addCommands(shootCommand);
invoker.addCommands(otherCOmmand);
invoker.sendCommands();
}
}
//命令
interface Command {
public void execute();
}
//射擊命令
class ShootCommand implements Command{
private Receiver receiver;
public ShootCommand(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
System.out.println("shootCommand is execute:");
receiver.action();
}
}
//其他命令
class OtherCommand implements Command{
private Receiver receiver;
public OtherCommand(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
System.out.println("otherCommand is execute:");
receiver.action();
}
}
//命令接受者(士兵)
class Receiver {
public String name;
public Receiver(String name){
this.name = name;
}
//行動,執行命令
public void action(){
System.out.println(name+" received the command!");
}
}
//命令呼叫者(司令官)
class Invoker {
private List<Command> commandList = new ArrayList<Command>();
public void addCommands(Command command){
this.commandList.add(command);
}
//發出命令
public void sendCommands(){
for(Command command : commandList){
command.execute();
System.out.println();
}
commandList.clear();
}
}
備忘錄模式
在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。
個人總結:建立一個備忘錄類,用來儲存原始類的資訊;同時建立備忘錄倉庫類,用來儲存備忘錄類,當然,原始類與備忘錄類的對應關係要處理好。
public class MementoDemo {
public static void main(String[] args) {
//待備份的類
Originator originator = new Originator();
originator.setState("123");
System.out.println("初始化的狀態為:"+originator.getState());
MementoStorage mementoStorage = new MementoStorage();
mementoStorage.add(originator.createMemento());
originator.setState("321");
System.out.println("修改後的狀態為:"+originator.getState());
originator.restoreMemento(mementoStorage.get(0));
System.out.println("還原後的狀態為:"+originator.getState());
}
}
//備忘錄類
class Memento {
private String state;
public Memento(String state){
this.state = state;
}
public String getState(){
return this.state;
}
}
//備忘錄類倉庫,備忘錄管理類
class MementoStorage {
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
//原始類
class Originator {
private String state;
public void setState(String state){
this.state = state;
}
public String getState(){
return this.state;
}
//建立備份
public Memento createMemento(){
//把需要備份的資訊全部儲存到備份類中。
return new Memento(state);
}
//還原備份
public void restoreMemento(Memento memento){
//把備份類中儲存的資訊還原
state = memento.getState();
}
}
狀態模式
允許物件在內部狀態發生改變時改變它的行為,物件看起來好像修改了它的類。
個人總結:物件具有多種狀態,且每種狀態具有特定的行為;應用場景: 行為隨狀態改變而改變的場景。程式碼形式似乎也和哪種設計模式相似,還是那句話,設計模式提倡的是思想,而不是形式。
public class StateDemo {
public static void main(String[] args) {
QQContext context = new QQContext();
//設定狀態,不同的狀態對應不同的行為
context.setState(new OnlineState());
context.getState().getMessage();
}
}
interface State{
public void getMessage();
}
//線上狀態(狀態物件)
class OnlineState implements State{
//線上狀態下的行為
public void getMessage(){
System.out.println("線上中,對好友可見!");
}
}
//隱身狀態(狀態物件)
class StealthState implements State{
//隱身狀態下的行為
public void getMessage(){
System.out.println("隱身中,對好友不可見!");
}
}
//QQ的登陸狀態類
class QQContext {
private State state;
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
訪問者模式
主要將資料結構與資料操作分離。
個人總結:在被訪問的類裡面加一個對外提供接待訪問者的介面(如下面例子的accept()方法)。訪問者封裝了對被訪問者結構的一些雜亂操作,避免這些操作"汙染"被訪問者,解耦結構與演算法,同時具有優秀的擴充套件性。
public class Visitor {
public static void main(String[] args) {
Computer computer = new Computer("myComputer");
//computer接受computerVisitor的訪問
computer.accept(new ComputerVisitor());
}
}
//被訪問者
class Computer {
private String computerName;
public String getComputerName(){
return computerName;
}
public Computer(String computerName){
this.computerName = computerName;
}
//提供接待訪問者的介面
public void accept(ComputerVisitor computerVisitor){
//訪問者訪問自身
computerVisitor.visit(this);
}
}
//訪問者
class ComputerVisitor {
//訪問Computer類,將被訪問者的引用傳入訪問者
public void visit(Computer computer){
System.out.println("訪問"+computer+"的name屬性:"+computer.getComputerName());
}
}
中介者模式
用一箇中介物件來封裝一系列的物件互動,中介者使各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。
個人總結:中介者物件,用來封裝關聯物件之間的互動操作,使關聯物件之間耦合度鬆散;例如,MVC模式中"控制器"就是"模型"和"檢視"的中介者;與介面卡模式的區別:介面卡模式為了橋接互不相容的介面,中介者為了分離原始結構和互動行為。
public class Mediator {
public static void main(String[] args) {
User1 user1 = new User1("小明");
User2 user2 = new User2("小紅");
UserMediator userMediator = new UserMediator(user1,user2);
userMediator.introduceYourselves();
}
}
class User1 {
private String name;
public String getName(){
return name;
}
public User1(String name){
this.name = name;
}
}
class User2 {
private String name;
public String getName(){
return name;
}
public User2(String name){
this.name = name;
}
}
//中介者,用來封裝User1與User2的互動操作
class UserMediator {
private User1 user1;
private User2 user2;
//將User1與User2傳入它們的中介者
public UserMediator(User1 user1, User2 user2){
this.user1 = user1; this.user2 = user2;
}
public void introduceYourselves(){
System.out.println("Hello "+user1.getName()+",I'm "+user2.getName());
System.out.println("Hi "+user2.getName()+",My name is "+user1.getName());
}
}
直譯器模式
給定一個語言,定義它的文法表示,並定義一個直譯器,這個直譯器使用該標識來解釋語言中的句子。
個人總結:自己定義一種語言或表示式(附對應的直譯器),用來表示一些複雜的頻繁發生的行為。例如:正則表示式的解釋,sql語句的解釋。使用場景極少,會引起效率、效能、維護等問題。
public class Interpreter {
public static void main(String[] args) {
Context context = new Context();
//建立自定義變數
Variable a = new Variable();
Variable b = new Variable();
//建立常量 3
Constant c = new Constant(3);
//給變數賦值
context.put(a, 5).put(b, 1);
//構建語法樹(自定義表示式:a+b+3)
Expression exp = new Add(new Add(a,b), c);
//解釋表示式a+b+3
int result = exp.interpret(context);
System.out.println("a+b+3 = "+result);
}
}
//表示式(所有表示式角色繼承該介面,自帶直譯器)
interface Expression {
//直譯器,解釋角色所需引數儲存在Context類中
public int interpret(Context context);
}
//構件環境類,包含直譯器之外的一些全域性資訊,一般是 HashMap。
class Context {
private Map<Variable,Integer> valueMap = new HashMap<Variable,Integer>();
public Context put(Variable x, int y){
valueMap.put(x, y);
return this;
}
public int get(Variable x){
int value = (Integer) valueMap.get(x);
return value;
}
}
//終結符表示式角色--常量
class Constant implements Expression{
private int a;
public Constant(int a){
this.a = a;
}
public int interpret(Context context){
return a;
}
}
//終結符表示式角色--變數
class Variable implements Expression{
public int interpret(Context context){
return context.get(this);
}
}
//非終結符表示式角色--運算子(+)
class Add implements Expression {
private Expression x, y;
public Add(Expression x, Expression y){
this.x = x; this.y = y;
}
public int interpret(Context context){
return x.interpret(context) + y.interpret(context);
}
}