UML
關聯關係(Association)
是類與類之間最常用的一種關係, 它是一種結構化關係,用於表示一類對象與另一類對象之間有聯繫。
單線實箭頭,可自關聯
關聯關係中要表明對象間的關聯關係,如是一對多還是一對一
依賴關係(Dependency)
一種使用關係,例如類A的某個方法需要類B做參數,即要使用類B的方法,用虛線表示
聚合關係(Aggregation)
表示一個整體與部分的關係,成員類是整體類的一部分,即成員對象是整體對象的一部分,但是成員對象可以脫離整體對象獨立 存在。聚合關係用帶空心菱形的直線表示。
組合關係(Composition)
也表示類之間整體和部分的關係, 但是組合關係中部分和整體具有統一的生存期。一旦整體對象不存在,部分對象也將不存在,部分對象與整體對象之間具有同生共死的關係。 如必須傳遞某個構造器參數以初始化類,用實心菱形的直線表示。
泛化關係(Generalization)
就是繼承關係,用帶空心三角形的直線來表示。
實現關係(Realization)
接口與實現的關係,用帶空心三角形的虛線來表示。
設計模式
對象創建型模式
面向對象設計原則
工廠方法模式
產品對象通過公共接口實現功能,抽象工廠聲明工廠方法由具體子類去實現,工廠的目的是爲了返回產品,抽象工廠聲明的方法要返回抽象產品,每一個具體產品都對應有一個具體工廠,具體工廠返回具體產品,利用多態的性質,全程使用接口對象聲明即可。
模式結構
• Product:抽象產品,產品對象的共同父類或接口
• ConcreteProduct:具體產品 ,實現了抽象產品接口
• Factory:抽象工廠,聲明工廠方法,交給子類實現
• ConcreteFactory:具體工廠,實現抽象工廠中的工廠方法,返回一個具體產品類的實例
利用多態的性質,將通用方法抽象出來放在接口中,調用時更加靈活
所有的具體工廠類都具有同一抽象父類。在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的接口,只要添加一個具體工廠和具體產品就可以了。這樣,系統的可擴展性也就變得非常好,完全符合“開閉原則”。
public interface Log {
void writeLog();
}
public class FileLog implements Log {
@Override
public void writeLog(){
System.out.println("我是文件日誌寫入器");
}
}
public class DataBaseLog implements Log {
@Override
public void writeLog(){
System.out.println("我是數據庫日誌寫入器");
}
}
public interface AbstractLogFactory {
Log createLog();
}
public class FileLogFactory implements AbstractLogFactory{
@Override
public Log createLog(){
return new FileLog();
}
}
public class DataBaseLogFactory implements AbstractLogFactory {
@Override
public Log createLog(){
return new DataBaseLog();
}
}
public class FactoryMethodPatternTest {
public static void main(String[] args){
AbstractLogFactory abstractLogFactory;
abstractLogFactory=new FileLogFactory();
abstractLogFactory.createLog().writeLog();
abstractLogFactory=new DataBaseLogFactory();
abstractLogFactory.createLog().writeLog();
}
}
運行結果爲:
我是文件日誌寫入器
我是數據庫日誌寫入器
抽象工廠模式
彌補工廠方法模式每一種產品都需要新增具體工廠的不足,抽象工廠中聲明多種產品工廠方法,能爲一個產品家族返回多種屬於該家族的具體產品。
當一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有對象時,抽象工廠模式比工廠方法模式更爲簡單、有效率。
模式結構:
• AbstractFactory:抽象工廠
• ConcreteFactory:具體工廠
• AbstractProduct:抽象產品
• Product:具體產品
同樣利用多態性質,符合開閉原則
public interface Connection {
boolean connection(String url,int port,String password);
}
public class OracleConnection implements Connection{
@Override
public boolean connection(String url,int port,String password){
System.out.println("已連接:"+url+port);
return true;
}
}
public class MySqlConnection implements Connection{
@Override
public boolean connection(String url,int port,String password){
System.out.println("已連接:"+url+port);
return true;
}
}
public interface Statement {
boolean getStatement();
}
public class OracleStatement implements Statement {
@Override
public boolean getStatement(){
System.out.println("create Oracle Statement");
return true;
}
}
public class MySqlStatement implements Statement {
public boolean getStatement(){
System.out.println("create MySql Statement");
return true;
}
}
public interface AbstractFactory {
Connection createConnection();
Statement createStatement();
}
public class OracleFactory implements AbstractFactory{
@Override
public Connection createConnection(){
return new OracleConnection();
}
@Override
public Statement createStatement(){
return new OracleStatement();
}
}
public class MySqlFactory implements AbstractFactory {
@Override
public Connection createConnection(){
return new MySqlConnection();
}
@Override
public Statement createStatement(){
return new MySqlStatement();
}
}
同樣使用多態的性質,如果使用工廠方法模式,那麼就需要建造兩個抽象工廠,四個具體工廠,因爲兩個抽象產品屬於同一個產品家族,所以只使用一個抽象工廠,把生產工廠的方法放在一起即可。缺點是難以擴展抽象產品,因爲必須屬於同一產品族的產品纔可以加入,爲了達到單一職責原則,不能在抽象工廠中任意添加產品
建造者模式
將部件和其組裝過程分開,一步一步創建一個複雜的對象。即將一個複雜對象的構建與它的表示分離。
也可能存在多個基本部分對象,基本部分對象通過不同的組合可以構建不同的對象,用戶使用不同的具體建造者即可得到不同的產品對象。
模式結構
• Director:指揮者 ,用於實際生產產品,內部有方法construct,規定一個產品的構造順序(即如何構造),通過對基本對象的組合生成一個複雜產品,內部維護建造者Builder,通過construct按順序建造產品
• Builder:抽象建造者 ,規定建造所有基本對象的方法
• ConcreteBuilder:具體建造者,實現每部分的建造方法
• Product:產品角色
建造者模式的目的仍然是爲了解耦,屏蔽某些複雜產品的實現,生成產品的組件由接口聲明,由具體的建造者建造,由Director調用組件組合順序生成產品,允許用戶只通過指定複雜對象的類型和內容就可以構建它們, 用戶不需要知道內部的具體構建細節。
//產品及組件
public class Product {
private ComponentA componentA;
private ComponentB componentB;
public Product(ComponentA componentA,ComponentB componentB){
this.componentA=componentA;
this.componentB=componentB;
}
@Override
public String toString(){
return "我是產品";
}
}
public class ComponentA {
@Override
public String toString(){
return "我是組件A";
}
}
public class ComponentB {
@Override
public String toString(){
return "我是組件B";
}
}
//建造者及實現
public interface Builder {
ComponentA buildA();
ComponentB buildB();
}
public class ConcreteBuilder implements Builder{
@Override
public ComponentA buildA(){
System.out.println("創建組件A");
return new ComponentA();
}
@Override
public ComponentB buildB(){
System.out.println("創建組件B");
return new ComponentB();
}
}
//指揮者,規定產品組合順序,調用建造者組裝成產品並返回
public class Director {
public static Product buildProduct(){
Builder builder=new ConcreteBuilder();
return new Product(builder.buildA(),builder.buildB());
}
}
public class builderPatternTest {
public static void main(String[] args){
Product product=Director.buildProduct();
System.out.println(product.toString());
}
}
/*運行結果
創建組件A
創建組件B
我是產品
*/
原型模式
對象創建型模式,用原型實例指定創建對象的種類,並且通過複製這些原型創建新的對象
在原型模式結構中定義了一個抽象原型類,所有的Java類都繼承 自java.lang.Object,而Object類提供一個clone()方法,可以將 一個Java對象複製一份。因此在Java中可以直接使用Object提供的clone()方法來實現對象的克隆,Java語言中的原型模式實現很簡單。 能夠實現克隆的Java類必須實現一個標識接口Cloneable,表示這個Java類支持複製。如果一個類沒有實現這個接口但是調用了 clone()方法,Java編譯器將拋出一個 CloneNotSupportedException異常。
淺克隆只克隆同一份對象的域的引用,深克隆開闢新的空間並賦值
java中的clone()是深克隆,但copy的內存中如果有引用變量,那麼引用的仍然是同一塊空間,如果想讓引用變量引用的空間也克隆一份,那麼最好重寫clone()或者自己另寫一個創建原型的方法
import java.util.Properties;
public class PrototypePatternTest {
public static void main(String[] args){
Properties properties=new Properties();
properties.put("a",new Properties());
Properties propertiesClone=(Properties) properties.clone();
System.out.println(properties==propertiesClone);
System.out.println(properties.getClass()==propertiesClone.getClass());
//clone的對象引用新的空間
properties.put("b",2);
System.out.println(propertiesClone.get("b"));
//但是內部的引用變量仍然引用同一塊空間
((Properties)properties.get("a")).put("b","2");
System.out.println(((Properties)propertiesClone.get("a")).get("b"));
}
}
/*結果爲
false
true
null
2
*/
使用原型模式可以簡化對象的創建過程,通過一個已有實例可以提高新實例的創建效率。
單例模式
單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱爲單例類,它提供全局訪問的方法。
單例模式的要點有三個:一是某個類只能有一個實例; 二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。
模式結構
• Singleton:單例
單例類擁有一個私有構造函數,確保用戶無法通過new關鍵字直接實例化它。除此之外,該模式中包含一個靜態私有成員變量與靜態公有的工廠方法,該工廠方法負責檢驗實例的存在性並實例化自己,然後存儲在靜態成員變量中,以確保只有一個實例被創建。
- 單例類的構造函數爲私有;
- 提供一個自身的靜態私有成員變量;
- 提供一個公有的靜態工廠方法。
確保只有一個對象被實例化的關鍵在於將構造器私有化,這樣外部無法使用new構建對象,也無法通過反射實例化對象,這樣一種設計思想確保系統中只有一個實例,提供關鍵服務,但對於該實例來說內部封裝的對象的操作在併發環境下仍要保持原子性,單例模式確保我只有我一個,最常用的便是提供序列化id,系統必須確保每次提供的是唯一id,因此採用單例模式+原子操作(靜態方法+原子操作也可以)。
public class Singleton {
private static Singleton singleton=new Singleton();
private static volatile long id;
//單例模式,私有化構造器,避免其他類new該對象
private Singleton(){
}
public static Singleton getSingleton() {
return singleton;
}
public synchronized long getId(){
return id++;
}
}
public class SingletonPatternTest {
public static void main(String[] args){
//錯誤,不能訪問私有
//Singleton singleton=new Singleton();
/*通過反射實例化將拋出java.lang.IllegalAccessException異常
try {
Singleton singleton=Singleton.class.newInstance();
}catch (Exception e){
e.printStackTrace();
}
*/
Singleton singleton=Singleton.getSingleton();
for(int i=0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"我的id:"+singleton.getId());
}
}).start();
}
}
}