OO基礎:
- 抽象
- 封裝
- 多態
- 繼承
OO設計特性:
- 可複用
- 可擴充
- 可維護
設計原則:
- 封裝變化:把會變化的部分取出並“封裝”起來,好讓其他部分不會受到影響(找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起)
- 針對接口編程,而不是針對實現編程
- 多用組合,少用繼承
- 爲了交互對象之間的鬆耦合設計而努力
- 類應該對擴展開放,對修改封閉
設計模式:
- 策略模式:定義了算法族,分別封裝起來,讓他們之前可以互相替換,此模式讓算法的變化獨立於使用算法的客戶
- 觀察者模式:定義了對象之前的一對多依賴,這樣一來,對一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新(java也內置了觀察者模式:參考java.util.Observer觀察者接口、被觀察者java.util.Observable接口)
- 裝飾者模式:動態的將責任附加到對象上,若有擴展功能,裝飾者提供了比繼承更有彈性的替代方案(前提:裝飾者和被裝飾對象有相同的超類型)、(java中的io大量使用裝飾者模式:)
- 簡單工廠模式:簡單工廠其實不是一個設計模式,反而更像一種編程習慣,定義一個生產工廠類,根據輸入類型生產對應的產品(利用靜態方法根據輸入參數生成對應的產品,隱藏了產品實例化的細節),但是當用戶需要新增產品ProductD時,必須在工廠類的生產方法中增加對應的判斷分支,所以簡單工廠模式違背了開放封閉原則
- 工廠模式:定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個,工廠方法讓類把實例化推遲到子類;符合開放封閉原則,但是由於每增加一個產品,都需要新增對應的生產工廠,導致增加額外的開發工作量
- 抽象工廠模式:提供一個接口,用於創建相關或依賴對象的家族,而不需要明確指定具體類
- 單例模式:一個類只有一個實例,並提供一個全局訪問點
- 適配器模式:將一個類的接口,轉換成客戶期望的另一個接口。適配器讓原本接口不兼容的類可以合作無間
難理解的模式下面給了示例,讀者根據示例更容易理解一點
代碼示例:
簡單工廠模式:
簡單工廠模式:
首先定義一個產品類的共同接口:
public interface Product{
//價格
int price();
//產品名
String getName();
}
分別有三個產品ProductA、ProductB、ProductC ,均實現Product接口:
public class ProductA implements Product {
@Override
public int price() {
return 100;
}
@Override
public String getName() {
return "ProductA";
}
}
public class ProductB implements Product {
@Override
public int price() {
return 200;
}
@Override
public String getName() {
return "ProductB";
}
}
public class ProductC implements Product {
@Override
public int price() {
return 300;
}
@Override
public String getName() {
return "ProductC";
}
}
定義一個生產工廠類,根據輸入類型生產對應的產品:
public class Factory {
public static Product createProduct(String type){
Product product =null;
switch (type){
case "A":
product = new ProductA();
break;
case "B":
product = new ProductB();
break;
case "C":
product = new ProductC();
break;
}
return product;
}
}
工廠模式:
首先聲明一個工廠接口,所有工廠必須實現這個接口:
public interface IFactory {
Product createProduct();
}
生產ProductA的工廠FactoryA:
public class FactoryA implements IFactory {
@Override
public Product createProduct() {
return new ProductA();
}
}
生產ProductB的工廠FactoryB:
public class FactoryB implements IFactory {
@Override
public Product createProduct() {
return new ProductB();
}
}
現在來根據新的工廠方法模式來生產:
IFactory factoryA = new FactoryA();
Product productA = factoryA.createProduct();
IFactory factoryB = new FactoryB();
Product productB = factoryB.createProduct();
抽象工廠模式:
假設現在需要針對每種產品生產對應的贈品,難道我們要新增一個Gift的生產工廠嗎?其實沒有必要,因爲在這個場景下,每種產品必須附帶了贈品,所以我們可以利用原有的工廠來生產贈品
先定一個共同的Gift接口:
public interface Gift {
String getGiftName();
}
增加GiftA、GiftB:
public class GiftA implements Gift {
@Override
public String getGiftName() {
return "GiftA";
}
}
修改Factory接口,增加生產Gift的方法:
public interface IFactory {
Product createProduct();
Gift createGift();
}
修改工廠方法模式下的FactoryA、FactoryB、FactoryC:
public class FactoryA implements IFactory {
@Override
public Gift createGift() {
return new GiftA();
}
@Override
public Product createProduct() {
return new ProductA();
}
}
生產產品和贈品:
IFactory factoryA = new FactoryA();
Product productA = factoryA.createProduct();
Gift giftA = factoryA.createGift();
裝飾者模式:
以點餐爲例,我在麥*勞點餐可以根據我需求增加食物種類和數量
抽象構件角色:
public interface Food {
String getDescription();
}
具體構件角色:
public class BasicSet implements Food{
@Override
public String getDescription() {
return "漢堡 + 可樂";
}
}
抽象裝飾類角色:
public abstract class Decorator implements Food {
private Food food;
public Decorator(Food food) {
this.food = food;
}
@Override
public String getDescription() {
return this.food.getDescription();
}
}
具體裝飾類角色:
public class FrenchFries extends Decorator {
public FrenchFries(Food food) {
super(food);
}
@Override
public String getDescription() {
return super.getDescription() + " + 薯條";
}
}
public class FriedChicken extends Decorator {
public FriedChicken(Food food) {
super(food);
}
@Override
public String getDescription() {
return super.getDescription() + " + 炸雞";
}
}
public class IceCream extends Decorator {
public IceCream(Food food) {
super(food);
}
@Override
public String getDescription() {
return super.getDescription() + " + 冰淇淋";
}
}
測試類:
public class Test {
public static void main(String[] args) {
Food food = new BasicSet();
Decorator setMealA = new FrenchFries(food);
setMealA = new FrenchFries(setMealA);
setMealA = new FriedChicken(setMealA);
setMealA = new IceCream(setMealA);
System.out.println("套餐A:" + setMealA.getDescription());
}
}
輸出結果:
套餐A:漢堡 + 可樂 + 薯條 + 薯條 + 炸雞 + 冰淇淋
單例模式:
第一種(懶漢,線程不安全):這種寫法lazy loading很明顯,但是致命的是在多線程不能正常工作
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
第二種(懶漢,線程安全):這種寫法能夠在多線程中很好的工作,而且看起來它也具備很好的lazy loading,但是,遺憾的是,效率很低,99%情況下不需要同步
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
第三種(餓漢):
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
第四種(餓漢,變種):
public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}
第五種(靜態內部類):
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
這種方式同樣利用了classloder的機制來保證初始化instance時只有一個線程,它跟第三種和第四種方式不同的是(很細微的差別):第三種和第四種方式是隻要Singleton類被裝載了,那麼instance就會被實例化(沒有達到lazy loading效果),而這種方式是Singleton類被裝載了,instance不一定被初始化。因爲SingletonHolder類沒有被主動使用,只有顯示通過調用getInstance方法時,纔會顯示裝載SingletonHolder類,從而實例化instance。想象一下,如果實例化instance很消耗資源,我想讓他延遲加載,另外一方面,我不希望在Singleton類加載時就實例化,因爲我不能確保Singleton類還可能在其他的地方被主動使用從而被加載,那麼這個時候實例化instance顯然是不合適的。這個時候,這種方式相比第三和第四種方式就顯得很合理
第六種(雙重校驗鎖):
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;
}
}
爲什麼要加volatile關鍵字來修飾singleton對象?因爲new Singleton()這塊存在重排序的問題,可能導致單例非單例,加上volatile後能讓jvm限制指令重排序,這樣就沒有問題了
適配器模式:
目標角色(PowerTarget.java):
public interface PowerTarget {
public int output5V();
}
適配者角色(PowerAdaptee.java):
public class PowerAdaptee {
private int output = 220;
public int output220V() {
System.out.println("電源輸出電壓:" + output);
return output;
}
}
適配器角色(PowerAdapter.java):
public class PowerAdapter implements PowerTarget{
private PowerAdaptee powerAdaptee;
public PowerAdapter(PowerAdaptee powerAdaptee) {
super();
this.powerAdaptee = powerAdaptee;
}
@Override
public int output5V() {
int output = powerAdaptee.output220V();
System.out.println("電源適配器開始工作,此時輸出電壓是:" + output);
output = output/44;
System.out.println("電源適配器工作完成,此時輸出電壓是:" + output);
return output;
}
}
參考文章:
https://www.cnblogs.com/heliusKing/p/11577837.html
https://www.cnblogs.com/mingmingcome/p/9810731.html