結構型模式
擴充卡模式(Adapter Pattern)
- 對象的擴充卡模式是各種結構型模式的起源
- 擴充卡模式: 将某個類的接口轉換成用戶端期望的另一個接口表示
- 目的: 消除由于接口不比對所造成的類的相容性問題
- 擴充卡模式主要分為三類:
- 類的擴充卡模式
- 對象的擴充卡模式
- 接口的擴充卡模式
- 核心思想: 有一個Source類,擁有一個方法待适配,目标接口是Targetable, 通過Adapter類,将Source的功能擴充到Targetable裡
- Source
public class Source{
public void method1(){
System.out.println("This is original method!");
}
}
- Targetable
public interface Targetable{
/* 與原類中的方法相同 */
public void method1();
/* 新類方法 */
public void method2();
}
- Adapter
public class Adapter extends Source implemments Targetable{
@Override
public void method2(){
System.out.println("This is the targetable method!");
}
}
- Adapter類繼承Source類,實作Targetable接口:
- AdapterTest
public class AdapterTest{
public static void main(String[] args){
Targetable target=new Adapter();
target.method1();
target.method2();
}
}
- 這樣Targetable接口的實作類就具有Source類的功能
- 基本思路和類的擴充卡相同,隻是将Adapter類作修改 ,不繼承Source類,而是持有Source類的執行個體,以達到解決相容性問題
- Wrapper
public class Wrapper implements Targetable{
private Source source;
public Wrapper(Source source){
super();
this.source=source;
}
@Override
public void method1(){
source.method1();
}
@override
public void method2(){
System.out.println("This is the targetable method!");
}
}
- Test
public class AdapterTest{
public static void main(String[] args){
Source source=new Source();
Targetable target=new Wrapper(source);
target.method1();
target.nethod2();
}
}
- 一個接口中有多個抽象方法,當寫該接口的實作類時,必須實作該接口的所有方法,這樣明顯比較浪費,因為并不是所有的方法都是需要用到的,有時隻要引入一些即可.為了解決這樣的問題,引入了接口擴充卡模式
- 接口擴充卡模式: 借助于一個抽象類,該抽象類實作了該接口以及所有的方法,隻需要和該抽象類進行聯系即可.
- 隻需要寫一個類,繼承該抽象類,重寫需要用到的方法
- Sourceable
public interface Sourceable{
public void method1();
public void method2();
}
- Wrapper-抽象類
public abstract class Wrapper implements Sourceable{
public void method1(){}
public void method2(){}
}
- SourceSub1
public class SourceSub1 extends Wrapper{
public void method1(){
System.out.println("The sourceable interface's first Sub");
}
}
- SourceSub2
public class SourceSub2 extends Wrapper(){
public void method2(){
System.out.println("The Sourceable interface's second Sub");
}
}
- WrapperTest
public class WrapperTest{
public static void main(String[] args){
Sourceable source1=new SourceSub1();
Sourceable source2=new SourceSub2();
source1.method1();
source1.method2();
source2.method1();
source2.method2();
}
}
- 三種擴充卡模式的應用場景:
- 類的擴充卡模式:
- 當希望一個類轉換成滿足另一個新接口的類時,可以使用類的擴充卡模式
- 建立一個新類,繼承原有的類,實作新的接口即可
- 對象的擴充卡模式:
- 當希望一個對象轉換成滿足另一個新接口的對象時,可以使用對象的擴充卡模式
- 建立一個Wrapper類,持有原類的一個執行個體,在Wrapper類的方法中,調用執行個體的方法即可
- 接口的擴充卡模式:
- 當不希望實作一個接口中所有的方法時,可以使用接口的擴充卡模式
- 建立一個抽象類Wrapper,實作所有方法,寫其它類時,隻要繼承抽象類即可
- 類的擴充卡模式:
裝飾器模式(Decorator)
- 裝飾器模式: 給一個對象動态地增加一些新的功能
- 裝飾器模式要求裝飾對象和被裝飾對象實作同一個接口, 裝飾對象持有被裝飾對象的執行個體
- Source類時被裝飾類 ,Decorator類是裝飾類,可以為Source類動态地增加一些功能:
- Sourceable
public interface Sourceable{
public void method();
}
- Source
public class Source implements Sourceable{
@Override
public void method(){
System.out.println("The original method!");
}
}
- Decorator
public class Decorator implements Sourceable{
private Sourceable source;
public Decorator(Sourceable source){
super();
this.source=source;
}
@Override
public void method(){
System.out.println("Before decorator!");
source.method();
System.out.println("After decorator!");
}
}
-Test
public class DecoratorTest{
public static void main(String[] args){
Sourceable source=new Source();
Sourceable obj=new Decorator(source);
obj.method();
}
}
- 裝飾器模式應用場景:
- 需要擴充一個類的功能
- 動态地為一個對象增加功能,而且還能動态地撤銷(繼承的功能是靜态的,不能動态增删)
- 裝飾器模式的缺點: 産生過多類似的對象,不易排錯
代理模式(Proxy)
- 代理模式: 建立一個代理類,替原對象進行一些操作
- Sourceable
public interface Sourceable{
public void method();
}
- Source
public class Source implements Sourceable{
@Override
public void method(){
System.out.println("The original method!");
}
}
- Proxy
public class Proxy implements Sourceable{
private Source source;
public Proxy(){
super();
this.source=new Source;
}
@Override
public void method(){
before();
source.method();
after();
}
public void before(){
System.out.println("Before Proxy!");
}
public void after(){
System.out.println("After Proxy!");
}
}
- ProxyTest
public class ProxyTest{
public static void main(String[] args){
Sourceable source=new Proxy();
source.method();
}
}
- 代理模式的應用場景:
- 已有的方法在使用的時候需要對原有的方法進行改進,有兩種方法:
- 修改原有的方法來适應: 這樣違反了"對擴充開放,對修改關閉"的原則 .不推薦使用
- 采用一個代理類調用原有的方法,且對産生的結果進行控制. 即代理模式
- 已有的方法在使用的時候需要對原有的方法進行改進,有兩種方法:
- 使用代理模式,可以将功能劃分的更加清晰,有助于後期維護
外觀模式(Facade)
- 在Spring中,可以将類與類之間的關系配置到配置檔案中
- 外觀模式: 為了解決類與類之間的依賴關系,将類魚雷之間的關系放到一個Facade類中,降低類與類之間的耦合度,該模式中沒有涉及到接口
- CPU
public class CPU{
public void startup(){
System.out.println("CPU startup!");
}
public void shutdown(){
System.out.println("CPU shutdown!");
}
}
- Memory
public class Memory{
public void startup(){
System.out.println("Memory startup!");
}
public void shutdown(){
System.out.println("Memory shutdown!");
}
}
- Disk
public class Disk{
public void startup(){
System.out.println("Disk startup!");
}
public void shutdown(){
System.out.println("Disk shutdown!");
}
}
- Computer
public class Computer{
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer(){
cpu=new CPU();
memory=new Memory();
disk=new Disk();
}
public void startup(){
System.out.println("Start the computer!");
cpu.startup();
memory.startup();
disk.startup();
System.out.println("Start the computer finished!");
}
public void shutdown(){
System.out.println("Begin to close the computer!");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("Computer closed!");
}
}
-User
public class User{
public static void main(String[] args){
Computer computer=new Computer();
computer.startup();
computer.shutdown();
}
}
- 如果沒有Computer類 ,CPU,Memory,Disk之間會互相持有執行個體,産生關系,這樣會造成嚴重依賴
- 修改一個類,可能會帶來其它類的修改
- 有了Computer類,各個類之間的關系就放在類Computer類裡,這樣就起到解耦的作用
橋接模式(Bridge)
- 橋接模式: 将事物和具體實作分開,二者可以各自獨立的變化
- 将抽象化與實作化解耦,使得二者可以獨立變化:
- JDBC橋DriverManager:
- JDBC連接配接資料庫的時候,在各個資料庫之間進行切換,基本不需要改動太多的代碼,甚至一點不用改動
- 原因在于JDBC提供統一接口,每個資料庫提供各自實作,用一個叫作資料庫驅動的程式來橋接即可
- JDBC橋DriverManager:
- Sourceable
public interface Sourceable{
public void method();
}
- SourceSub1
public class SourceSub1 implements Sourceable{
@Override
public void method(){
System.out.println("This is the first sub!");
}
}
- SourceSub2
public class SourceSub2 implements Sourceable{
@Override
public void method(){
System.out.println("This is the second sub!");
}
}
- 定義一個橋,持有Sourceable的一個執行個體
public abstract class Bridge{
private Sourceable source;
public void method(){
source.method();
}
public Sourceable getSource(){
return source;
}
public void getSource(Sourceable source){
this.source=source;
}
}
- MyBridge
public class MyBridge extends Bridge{
public void method(){
getSource().method();
}
}
- BridgeTest
public class BridgeTest{
public static void main(String[] args){
Bridge bridge=new MyBridge();
/* 調用第一個對象 */
Sourceable source1=new SourceSub1();
bridge.setSource(source1);
bridge.method();
/* 調用第二個對象 */
Sourceable source2=new SourceSub2();
bridge.setSource(source2);
bridge.method();
}
}
- 通過對Bridge類的調用,實作了對接口Sourceable的實作類SourceSub1和SourceSub2的調用
- 示例: JDBC連接配接原理
組合模式(Composite)
- 組合模式: 部分-整體模式,在處理類似樹形結構的問題時比較友善
- TreeNode
public class TreeNode{
private String name;
private TreeNode parent;
private Vector<TreeNode> children=new Vector<TreeNode>();
public TreeNode(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public TreeNode getParent(){
return parent;
}
public void setParent(TreeNode parent){
this.parent=parent;
}
/* 添加孩子節點 */
public void add(TreeNode node){
children.add(node);
}
/* 删除孩子節點 */
public void remove(TreeNode node){
children.remove(node);
}
/* 獲得孩子節點 */
public Enumeration<TreeNode> getChildren(){
return children.elements();
}
}
- Tree
public class Tree{
TreeNode root=null;
public Tree(String name){
root=new TreeNode(name);
}
public void main(String[] args){
Tree tree=new Tree("A");
TreeNode nodeB=new TreeNode("B");
TreeNode nodeC=new TreeNode("C");
nodeB.add(nodeC);
tree.root.add(nodeB);
System.out.println("Build the tree finished!");
}
}
- 組合模式使用場景:
- 将多個對象組合在一起進行操作
- 常用于表示樹形結構中:二叉樹
享元模式
- 享元模式: 主要目的是實作對象共享,即共享池
- 當系統中對象多的時候可以減少記憶體的開銷,通常與工廠模式一起使用
- FlyWeightFactory: 負責建立和管理享元單元
- 當一個用戶端請求時,工廠需要檢查目前對象池中是否有符合條件的對象
- 如果有,就傳回已經存在的對象
- 如果沒有,就建立一個新對象
- FlyWeight: 超類
- 共享的對象的特點:
- 共享對象有一些共同的屬性
- 這些屬性對于每個連接配接來說都是一樣的
- 基于共享對象的特點,可以用享元模式處理共享對象:
- 将類似屬性作為内部資料
- 其它的屬性作為外部資料
- 在方法調用時,當作參數傳進來
- 這樣可以節省記憶體空間,減少執行個體的數量
- 示例: 資料庫連接配接池
public class ConnectionPool{ private Vector<Connection> pool; /* 公有屬性 */ private String url="jdbc:mysql://localhost:3306/test"; private String username="root"; private String password="root"; private String driverClassName="com.mysql.jdbc.Driver"; private int poolSize=100; private static ConnectionPool instance=null; Connection conn=null; /* 構造方法,負責初始化 */ private ConnectionPool(){ pool = new Vector<Connection>(poolSize); for(int i=0;i<poolSize;i++){ try{ Class.forName(driverClassName); conn=DriverManager.getConnection(url,user,password); pool.add(conn); }catch(ClassNotFoundException e){ e.printStackTrace(); }catch(SQLEXception e){ e.printStackTrace(); }
}
/* 傳回連接配接到連接配接池 */
public sysnchronized void release(){
pool.add(conn);
}
/* 傳回連接配接池中的一個資料庫 */
public syschronized Connection getConnection(){
if(pool.size()>0){
Connection conn=pool.get(0);
pool.remove(conn);
return conn;
}else{
return null;
}
}
- **通過連接配接池的連接配接,實作資料庫連接配接的共享:**