一栓霜、設(shè)計模式的分類
什么是設(shè)計模式:在某些場景下翠桦,針對某類問題的某種通用的解決方案
特點: 通用、可復用
設(shè)計模式有哪些
-
創(chuàng)建型模式 :對象實例化的模式胳蛮,用于解耦對象的實例化過程销凑。5種
工廠方法模式、抽象工廠模式仅炊、單例模式斗幼、建造者模式、原型模式
-
結(jié)構(gòu)型模式 :把類或?qū)ο蠼Y(jié)合在一起形成一個更大的結(jié)構(gòu)茂洒。7種
適配器模式孟岛、裝飾器模式、代理模式督勺、外觀模式渠羞、橋接模式、組合模式智哀、享元模式
-
行為型模式 : 類與對象交互次询,責任劃分及算法。11種
策略模式瓷叫、模板方法模式屯吊、觀察者模式、迭代子模式摹菠、責任鏈模式盒卸、命令模式、備忘錄模式次氨、狀態(tài)模式蔽介、訪問者模式、中介者模式煮寡、解釋器模式
共23種 - 額外兩種:
并發(fā)型模式, 線程池模式
二虹蓄、設(shè)計模式的六大原則
總原則:開閉原則(Open Close Principle)
對擴展開放,對修改關(guān)閉幸撕。使程序的擴展性好锥咸,易于維護和升級, 使用接口和抽象類等, 實現(xiàn)熱插拔效果约啊。
1、單一職責原則
每個類應該實現(xiàn)單一的職責,解耦锌妻。
2、里氏替換原則(Liskov Substitution Principle)
對“開-閉”原則的補充序六,對實現(xiàn)抽象化的具體步驟的規(guī)范糕非。
子類對父類的方法盡量不要重寫和重載。因為父類代表了定義好的結(jié)構(gòu)站叼,通過這個規(guī)范的接口與外界交互娃兽,子類不應該隨便破壞它。
3尽楔、依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)
開閉原則的基礎(chǔ)投储,面向接口編程,依賴于抽象而不依賴于具體阔馋。寫代碼時用到具體類時玛荞,不與具體類交互,而與具體類的上層接口交互呕寝。
4勋眯、接口隔離原則(Interface Segregation Principle)
每個接口中不存在子類用不到卻必須實現(xiàn)的方法,如果不然,就要將接口拆分客蹋。使用多個隔離的接口塞蹭,比使用單個接口(多個接口方法集合到一個的接口)要好。
5讶坯、迪米特法則(最少知道原則)(Demeter Principle)
一個類對自己依賴的類知道的越少越好番电。無論被依賴的類多么復雜,都應將邏輯封裝在方法的內(nèi)部辆琅,通過public方法提供給外部漱办。這樣當被依賴的類變化時,才能最小的影響該類婉烟。
6娩井、合成復用原則(Composite Reuse Principle)
盡量首先使用聚合的方式,而不是使用繼承
常見模式介紹
1) 單例模式
它核心結(jié)構(gòu)只包含一個被稱為單例類的特殊類似袁。通過單例模式可以保證系統(tǒng)中一個類只有一個實例而且該實例易于外界訪問撞牢,從而方便對實例個數(shù)的控制并節(jié)約系統(tǒng)資源。
應用場景:如果希望在系統(tǒng)中某個類的對象只能存在一個叔营,單例模式是最好的解決方案屋彪。
懶漢式
class LazyMan{
private static LazyMan lm;
private LazyMan(){};
public static LazyMan getInstance(){
if (lm == null){
synchronized (LazyMan.class) {
if (lm == null) {
lm = new LazyMan();
}
}
}
return lm;
}
}
餓漢式
class HungeryMan{
private static HungeryMan hm;
private HungeryMan(){
hm = new HungeryMan();
};
public static HungeryMan getInstance(){
return hm;
}
}
2) 工廠方法模式
工廠模式主要是為創(chuàng)建對象提供了接口。
應用場景:
a绒尊、 在編碼時不能預見需要創(chuàng)建哪種類的實例畜挥。
b、 系統(tǒng)不應依賴于產(chǎn)品類實例如何被創(chuàng)建婴谱、組合和表達的細節(jié)蟹但。
簡單工廠模式不屬于23種設(shè)計模式范疇。
簡單工廠模式的問題:類的創(chuàng)建依賴工廠類谭羔,若想要拓展程序华糖,必須對工廠類進行修改,這違背了閉包原則瘟裸,所以客叉,就有了工廠方法模式,創(chuàng)建一個工廠接口和創(chuàng)建多個工廠實現(xiàn)類话告,一旦需要增加新的功能兼搏,直接增加新的工廠類,不需要修改之前的代碼沙郭。
一個頂級接口
public interface Sender {
public void Send();
}
兩個實現(xiàn)類:
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
兩個工廠類:
public class SendMailFactory implements Provider {
@Override
public Sender produce(){
return new MailSender();
}
}
[java] view plaincopy
public class SendSmsFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
再提供一個接口:
public interface Provider {
public Sender produce();
}
測試類:
public class Test {
public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}
3) 策略模式
定義了算法族佛呻,分別封裝起來,讓它們之間可以互相替換病线。此模式讓算法的變化獨立于使用算法的客戶吓著。
應用場景:
a鲤嫡、 一件事情,有很多方案可以實現(xiàn)绑莺。
b泛范、我可以在任何時候,決定采用哪一種實現(xiàn)紊撕。
c.、未來可能增加更多的方案赡突。
d对扶、 策略模式讓方案的變化不會影響到使用方案的客戶。
策略模式的決定權(quán)在用戶惭缰,系統(tǒng)本身提供不同算法的實現(xiàn)浪南,新增或者刪除算法,對各種算法做封裝漱受。因此络凿,策略模式多用在算法決策系統(tǒng)中,外部用戶只需要決定用哪個算法即可昂羡。
需要設(shè)計一個接口絮记,為一系列實現(xiàn)類提供統(tǒng)一的方法,多個實現(xiàn)類實現(xiàn)該接口虐先≡狗撸可以設(shè)計一個抽象類(輔助類可有可無),提供輔助函數(shù)
首先統(tǒng)一接口:
public interface ICalculator {
public int calculate(String exp);
}
輔助類:
public abstract class AbstractCalculator {
public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}
三個實現(xiàn)類:
public class Plus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"\\+");
return arrayInt[0]+arrayInt[1];
}
}
public class Minus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"-");
return arrayInt[0]-arrayInt[1];
}
}
public class Multiply extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"\\*");
return arrayInt[0]*arrayInt[1];
}
}
簡單的測試類:
public class StrategyTest {
public static void main(String[] args) {
String exp = "2+8";
ICalculator cal = new Plus();
int result = cal.calculate(exp);
System.out.println(result);
}
}
輸出:10
4) 觀察者模式蛹批。
又被稱作發(fā)布/訂閱模式撰洗,定義了對象間一對多依賴,當一個對象改變狀態(tài)時腐芍,它的所有依賴者都會收到通知并自動更新差导。
應用場景:
a、對一個對象狀態(tài)的更新猪勇,需要其他對象同步更新设褐,而且其他對象的數(shù)量動態(tài)可變。
b泣刹、對象僅需要將自己的更新通知給其他對象而不需要知道其他對象的細節(jié)络断。
一個Observer接口:
public interface Observer {
public void update();
}
兩個實現(xiàn)類:
public class Observer1 implements Observer {
@Override
public void update() {
System.out.println("observer1 has received!");
}
}
public class Observer2 implements Observer {
@Override
public void update() {
System.out.println("observer2 has received!");
}
}
Subject接口及實現(xiàn)類:
public interface Subject {
/*增加觀察者*/
public void add(Observer observer);
/*刪除觀察者*/
public void del(Observer observer);
/*通知所有的觀察者*/
public void notifyObservers();
/*自身的操作*/
public void operation();
}
public abstract class AbstractSubject implements Subject {
private Vector<Observer> vector = new Vector<Observer>();
@Override
public void add(Observer observer) {
vector.add(observer);
}
@Override
public void del(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<Observer> enumo = vector.elements();
while(enumo.hasMoreElements()){
enumo.nextElement().update();
}
}
}
public class MySubject extends AbstractSubject {
@Override
public void operation() {
System.out.println("update self!");
notifyObservers();
}
}
測試類:
public class ObserverTest {
public static void main(String[] args) {
Subject sub = new MySubject();
sub.add(new Observer1());
sub.add(new Observer2());
sub.operation();
}
}
輸出:
update self!
observer1 has received!
observer2 has received!
5) 裝飾器模式
給一個對象增加一些新的功能,而且是動態(tài)的项玛,要求裝飾對象和被裝飾對象實現(xiàn)同一個接口貌笨,裝飾對象持有被裝飾對象的實例
應用場景:
a、需要擴展一個類的功能襟沮。
b锥惋、動態(tài)的為一個對象增加昌腰、撤銷功能。(繼承的功能是靜態(tài)的膀跌,不能動態(tài)增刪遭商。)
缺點:產(chǎn)生過多相似的對象,不易排錯
接口
public interface Sourceable {
public void method();
}
原始實現(xiàn)類
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
代理類
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!"); //裝飾器
}
}
測試類:
public class DecoratorTest {
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
}
輸出:
before decorator!
the original method!
after decorator!
6) 代理模式
給一個對象提供一個代理捅伤,并由代理對象控制對原對象的引用劫流。它使得客戶不能直接與真正的目標對象通信。
代理對象是目標對象的代表丛忆,其他需要與這個目標對象打交道的操作都是和這個代理對象在交涉祠汇,起到中介的作用,既保護了目標對象的熄诡,同時也在一定程度上面減少了系統(tǒng)的耦合度可很。
應用場景:
如果已有的方法在使用的時候需要對原有的方法進行改進,此時有兩種辦法:
a凰浮、修改原有的方法來適應我抠。這樣違反了“對擴展開放,對修改關(guān)閉”的原則袜茧。
b菜拓、采用一個代理類調(diào)用原有的方法,對產(chǎn)生的結(jié)果進行控制笛厦。
使用代理模式尘惧,可以將功能劃分的更加清晰,有助于后期維護
接口
public interface Sourceable {
public void method();
}
原始實現(xiàn)類
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
代理類
public class Proxy implements Sourceable {
private Source source;
public Proxy(){
super();
this.source = new Source();
}
@Override
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
測試類:
public class ProxyTest {
public static void main(String[] args) {
Sourceable source = new Proxy();
source.method();
}
}
輸出:
before proxy!
the original method!
after proxy!
代理模式递递,偏重因自己無法完成或自己無需關(guān)心喷橙,需要他人干涉事件流程,更多的是對對象的控制登舞。
裝飾模式贰逾,偏重對原對象功能的擴展,擴展后的對象仍是是對象本身菠秒。
7) 迭代子模式疙剑。
提供一種方法順序訪問一個聚合對象中各個元素,而又不暴露該對象的內(nèi)部表示践叠。
應用場景:
當你需要訪問一個聚集對象言缤,而且不管這些對象是什么都需要遍歷的時候,就應該考慮用迭代器模式禁灼。其實stl容器就是很好的迭代器模式的例子管挟。
兩個接口
public interface Collection {
public Iterator iterator();
/*取得集合元素*/
public Object get(int i);
/*取得集合大小*/
public int size();
}
public interface Iterator {
//前移
public Object previous();
//后移
public Object next();
public boolean hasNext();
//取得第一個元素
public Object first();
}
兩個實現(xiàn):
public class MyCollection implements Collection {
public String string[] = {"A","B","C","D","E"};
@Override
public Iterator iterator() {
return new MyIterator(this);
}
@Override
public Object get(int i) {
return string[i];
}
@Override
public int size() {
return string.length;
}
}
public class MyIterator implements Iterator {
private Collection collection;
private int pos = -1;
public MyIterator(Collection collection){
this.collection = collection;
}
@Override
public Object previous() {
if(pos > 0){
pos--;
}
return collection.get(pos);
}
@Override
public Object next() {
if(pos<collection.size()-1){
pos++;
}
return collection.get(pos);
}
@Override
public boolean hasNext() {
if(pos<collection.size()-1){
return true;
}else{
return false;
}
}
@Override
public Object first() {
pos = 0;
return collection.get(pos);
}
}
測試類:
public class Test {
public static void main(String[] args) {
Collection collection = new MyCollection();
Iterator it = collection.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
輸出:A B C D E
8) 模板方法模式
定義一個操作中的算法的骨架,將一些步驟延遲到子類中弄捕,模板方法使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些步驟僻孝。
應用場景:
對于一些功能导帝,在不同的對象身上展示不同的作用,但是功能的框架是一樣的穿铆。
public abstract class AbstractCalculator {
/*主方法您单,實現(xiàn)對本類其它方法的調(diào)用*/
public final int calculate(String exp,String opt){
int array[] = split(exp,opt);
return calculate(array[0],array[1]);
}
/*被子類重寫的方法*/
abstract public int calculate(int num1,int num2);
public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}
public class Plus extends AbstractCalculator {
@Override
public int calculate(int num1,int num2) {
return num1 + num2;
}
}
測試類:
public class StrategyTest {
public static void main(String[] args) {
String exp = "8+8";
AbstractCalculator cal = new Plus();
int result = cal.calculate(exp, "\\+");
System.out.println(result);
}
}
輸出:10