前段時間看到一篇關於java23種設計模式的博文講解,講的蠻不錯的,也有案例,之前也有看過設計模式的書,但沒有細讀,也沒有記錄下來。總感覺不是自己的東西,所以這裏終於下定決心細細研究一番。其實,最終讀完發現雖然不知道前人總結的設計模式名字,但是在實際編碼中其實已經用到了很多。這也說明了設計模式是總結出來的經驗,不用刻意使用,當有這方面需求的時候,我們自然而然就想到了,當然了這也需要見過和寫過很多代碼。
這裏是引用 https://www.jianshu.com/p/5bdb76933cd4
先介紹個 開閉原則:對擴展開放,對修改關閉。 原則目的是在不修改原代碼的情況下,可以擴展新代碼,滿足新需求。就測試而言,原代碼沒有變就不要重複測試,只測新增代碼即可。
大類一,創建者模式
- 工廠模式
這種模式我也就是很早之前學習java時用到過,後面就沒有用過了,很雞肋,因爲雖然創建類的代碼集中到了一起,層級比較清晰,但代碼量太大。
下面例子中介紹了一個球類接口,比作一個工廠,在此接口下創建各種球,如籃球,排球等,每個實例都是在工廠下創建的。
public interface Ball{
public String create();
}
public class Soccer implements Ball{
@override
public String create(){
return "give you a soccer";
}
}
public class Basketball implements Ball{
@override
public String create(){
return "give you a Basketball ";
}
}
public class BallFactory{
public static Ball createBall(String name){
if(name.equals("basketball")){
return new Basketball();
}else if(name.equals("soccer")){
return new Soccer();
}else{
return null;
}
}
public static void mian(String[] args){
Ball soccer=BallFactory.createBall("soccer");
Ball basketball=BallFactory.createBall("basketball");
System.out.println(soccer.create());
System.out.println(basketball.create());
}
}
2.抽象工廠模式
是對工廠模式的升級,球工廠不但能生成球,還能生產關於球的副產品,如雜誌。
代碼如下:
package test;
public class Factorymodel {
public static void main(String[] args) {
// TODO Auto-generated method stub
SoccerFactory soccerFactory=new SoccerFactory();
Ball soccer=soccerFactory.create();
System.out.println(soccer.create());
//System.out.println(soccer.aa());//不是ball裏面的方法會報錯,所以把不同的實現結果再賦值給接口類時只能接收接口有的方法
}
}
interface BallFactory{
public Ball create();
public Journal crate();
}
interface Journal{
public String create();
}
interface Ball{
public String create();
}
class Soccer implements Ball{
@Override
public String create(){
return "give you a soccer";
}
public int a;
public int aa(){
return 2;
}
}
class SoccerJournal implements Journal{
@Override
public String create() {
// TODO Auto-generated method stub
return "give you a soccer Jounal";
}
}
class SoccerFactory implements BallFactory{
@Override
public Ball create() {
// TODO Auto-generated method stub
return new Soccer();
}
@Override
public Journal crate() {
// TODO Auto-generated method stub
return new SoccerJournal();
}
}
可以看出代碼量很大,我們來講講各個類的作用吧,首先是三個接口,接口的作用是強制規範代碼和將不同的實現類結果通過接口類輸出。重點是三個實現類,對於Soccer 和SoccerJournal 這兩個實現類就好比兩個只生產足球和只生產足球雜誌的工廠。對於SoccerFactory 實現類就好比經銷商,它不事生產,而是將各個工廠已經加工好的產品拿過來賣出去。這就比較明瞭了,銷售與工廠分開,工廠之間有隻專注於各自的產品。銷售與工廠上面又都有各自的接口規範。
- 單利模式
這個大家都很熟悉了,就是爲了保證某個對象只有一個實例存在,無論在哪裏修改,只對這一個對象做操作。
class Singlton{
//使用volatile,使所有線程可見。
private static volatile Singlton singlton;
//避免外部類實例化,注意是私有的,這樣外面的類就不能new了
private Singlton(){
}
public static synchronized Singlton getInstance(){
if(singlton!=null){
return new Singlton();
}else{
return singlton;
}
}
}
- 建造者模式
就是將一個複雜對象,分解成很多簡單的對象。比如我做過的徵信報告,裏面的子節點很多。就需要分爲好多子類處理。
interface CPU{
}
//interCPU是CPU衆多種類中的一種
class InterCPU implements CPU{
}
interface MainBoard{
}
class AmainBoard implements MainBoard{
}
class Computer{
private CPU cpu;//CPU
private MainBoard mainBoard;//主板
public CPU getCpu() {
return cpu;
}
public void setCpu(CPU cpu) {
this.cpu = cpu;
}
public MainBoard getMainBoard() {
return mainBoard;
}
public void setMainBoard(MainBoard mainBoard) {
this.mainBoard = mainBoard;
}
}
//建造者
abstract class Builder{
abstract void buildcpu();
abstract void buildMainBoard();
}
//具體建造者
class ConcreteBuilder extends Builder{
Computer computer=new Computer();
@Override
void buildcpu() {
// TODO Auto-generated method stub
computer.setCpu(new InterCPU());
}
@Override
void buildMainBoard() {
// TODO Auto-generated method stub
computer.setMainBoard(new AmainBoard());
}
}
測試類
@Test
public void testBuild(){
//建造者
ConcreteBuilder builder=new ConcreteBuilder();
builder.buildcpu();
System.out.println(builder.computer.getCpu()==null);//false 因爲上一步創建了
System.out.println(builder.computer.getMainBoard()==null);//true 因爲上一步沒有創建
}
總結,這個在官網上說跟工廠模式的區別是,工廠注重create的過程,而建造者注重combine組裝過程。從最後的測試類中我們也可以看出是這麼點意思。需要電腦中哪些組件就創建哪些,而組件的具體怎麼造出來的沒有介紹。其實再往下寫就是工廠模式了。二者是前後關係,工廠造好了東西,建造者組裝起來。
再者,我個人才行spring容器加載初始化對象的時候用的應該就是建造者模式,當有一個很大的類需要new時,我們不需要一步步去分別new其下面的小類,而是直接new大類,spring容器會自動給我們創建其中的小類。
- 原型模式
適用於用一個已經創建出來的實例作爲原型,通過複製該實例創建出一個與之相似的新對象。
原型模式的克隆有淺克隆和深克隆,淺克隆是大類中的引用類不做克隆,指向的是同一個引用,深克隆是引用類也克隆,這樣引用地址就變了
只要實現Cloneable接口就可實現對象的淺克隆
淺克隆:
class User implements Cloneable{
String name="比比";
public Computer2 computer;
protected User clone() {
User user=new User();
// user.name=co
return user;
}
}
class Computer2 {
}
測試類:
@Test
public void testClone(){
User user1=new User();
user1.name="哈哈";
User user2=user1.clone();
user2.name="釘釘";
System.out.println(user1.name);//哈哈
System.out.println(user2.name);//比比
System.out.println(user1.name);//哈哈
System.out.println(user1==user2);//false
System.out.println(user1.computer==user2.computer);//true
}
重點是最後一行的computer小類在user1和user2中是同一個引用。
深克隆 :
class User implements Cloneable{
String name="比比";
public Computer2 computer;
protected User clone() {
User user=new User();
user.computer=computer.clone();
return user;
}
}
class Computer2 implements Cloneable{
protected Computer2 clone() {
return new Computer2();
}
}
測試類:
@Test
public void testClone(){
User user1=new User();
user1.computer=new Computer2();//此處創建了一個小類,但是在下面的user1.clone中不會把這個克隆過去
user1.name="哈哈";
User user2=user1.clone();
user2.name="釘釘";
System.out.println(user1.name);//哈哈
System.out.println(user2.name);//比比
System.out.println(user1.name);//哈哈
System.out.println(user1==user2);//false
System.out.println(user1.computer==user2.computer);//false
}
可以看出無論是淺克隆還是深克隆都是複製最初的那個對象,可以理解爲clone的是大類中clone的內容。
大類二:結構型模式
設計模式是幫助我們清晰的創建對象,而結構型模式則注重於對象關係之間交互。
- 代理模式
就是有個中介在客戶和生產者這之間做溝通。
案例:
interface Buy{
public void bugCar();
}
class People implements Buy{
@Override
public void bugCar() {
// TODO Auto-generated method stub
System.out.println("小王get a cat");
}
}
class ProxyPeople implements Buy{
private People people;
public ProxyPeople() {
// TODO Auto-generated constructor stub
this.people=new People();
}
@Override
public void bugCar() {
// TODO Auto-generated method stub
System.out.println("4s點幫你上稅,上保險,提車");
people.bugCar();
}
}
@Test
public void ProxyTest(){
//可以看出測試類只能看出是代理商在買車,而真正買車的人不知道,但是人家已經買到了車
Buy buy=new ProxyPeople();
buy.bugCar();//4s點幫你上稅,上保險,提車 //小王get a cat
}
總結,代理模式用的地方還是很多的,如通訊時不想讓別人知道你的ip可以使用代理模式隱藏你的信息,還有spring的動態代理,用於日誌打印,與數據庫的交互自動連接和關閉以及回滾服務等。
- 適配器模式
顧名思義,是讓兩個不兼容的東西可以在一起工作,如變壓器的原理,讓不同電壓的電器可以相互連接。而在代碼上就是如果一個接口後很多方法,我只要實現其中的一兩個,但是還是要重寫其他沒用到的方法,這時候就需要適配器從中間中轉一下,實現目標類與適配者的解耦。
interface ATopInter{
public void one();
public void two();
public void three();
}
class Adapter implements ATopInter{
@Override
public void one() {
// TODO Auto-generated method stub
System.out.println("sdsd");
}
@Override
public void two() {
// TODO Auto-generated method stub
}
@Override
public void three() {
// TODO Auto-generated method stub
}
}
class You extends Adapter{
public void one(){
super.one();
System.out.println("one");
}
}
@Test
public void AdapterTest(){
You you =new You();
you.one();//sdsd//one
}
- 橋接模式
將抽象和實現分離,使他們可以獨立變化,即取消二者的繼承關係,改爲組合關係。
這裏舉個不太恰當的例子,由不同的cpu可組成不同品牌的電腦,所以有如下代碼:
abstract class ComputerBread{
abstract void cupType();
}
class Intercpu extends ComputerBread{
@Override
void cupType() {
// TODO Auto-generated method stub
System.out.println("intercpu的品牌");
}
}
class amdcpu extends ComputerBread{
@Override
void cupType() {
// TODO Auto-generated method stub
System.out.println("amdcpu的品牌");
}
}
class Computer3{
private ComputerBread computerBread;
public Computer3(ComputerBread computerBread){
this.computerBread=computerBread;
}
public void cupType(){
if(computerBread!=null){
computerBread.cupType();
}
}
}
@Test
public void bridge(){
Computer3 computer3=new Computer3(new Intercpu());
computer3.cupType();//intercpu的品牌
}
總結,這裏實現了給電腦分配不同的cpu就形成了不同的電腦品牌,說實在的沒太看懂,有點多態的意思。簡而言之,就是給某個物體加上不同的修飾,就形成了不同的狀態。這裏物體就是橋接模式中術語的抽象化角色,各種修飾就是實現化角色。
- 裝飾模式
跟代理模式很像,舉個例子:
我們以手抓餅爲例,有原味的,在此基礎上可加雞蛋,雞肉等
抽象構件(component)角色
//抽象構建
interface Cake{
public String money();
public String description();
}
//具體構建
class Shreddedcake implements Cake{
@Override
public String money() {
// TODO Auto-generated method stub
return "3.5";
}
@Override
public String description() {
// TODO Auto-generated method stub
return "原味手抓餅";
}
}
//裝飾角色
abstract class Deractor implements Cake{
Cake cake;
public Deractor(Cake cake){
this.cake=cake;
}
public String money(){
return cake.money();
}
public String description(){
return cake.description();
}
}
//具體裝飾類角色
class Eggshrededcake extends Deractor{
public Eggshrededcake(Cake cake) {
super(cake);
// TODO Auto-generated constructor stub
}
@Override
public String description() {
// TODO Auto-generated method stub
return "雞蛋"+cake.description();
}
@Override
public String money() {
// TODO Auto-generated method stub
return "1.5"+cake.money();
}
}
@Test
public void decoratorTest(){
Shreddedcake shreddedcake=new Shreddedcake();
System.out.println(shreddedcake.description());//原味手抓餅
Eggshrededcake eggshrededcake=new Eggshrededcake(shreddedcake);
System.out.println(eggshrededcake.description());//雞蛋原味手抓餅
}
總結,可見裝飾類Deractor承擔了中介的角色,跟代理模式很像。但是在雞蛋手抓餅和原味手抓餅在這裏不是繼承的關係,這點又有點像橋接模式的組合關係。用官網的話來說:裝飾模式是在不改變現有對象的結構下,動態的添加一些職能,這種功能擴展模式比採用繼承方式更加靈活。
- 外觀模式
是一種無須讓對象知道系統內部實現的複雜度,儘量讓用戶感知到是非常簡單的,比如springmvc的controller層,只是提供一個接口,真正的業務邏輯是在service層,這個模式還是用的非常多的,比如一個工具類的方法,我們只需傳參調用即可,讓一個個方法獨立起來,降低耦合,精簡代碼。
class A{
public void method(){
System.out.println("方法一");
}
}
class B{
public void method(){
System.out.println("method2");
}
}
class C{
public void method(){
System.out.println("method3");
}
}
class Facade{
public A a=new A();
public B b=new B();
public C c=new C();
public void method(){
a.method();
b.method();
c.method();
}
}
@Test
public void FacadeTest(){
Facade facade=new Facade();
facade.method();
}
總結,A B C 三個類通過外觀類facade 訪問。
- 組合模式
是將存在某種包含關係的數據組織在一起,典型的例子就是樹狀結構,下面舉例說明,對一個樹狀結構的樹枝上的樹葉做增刪操作。在測試類中我們給兩個樹枝分別加幾個樹葉。
//抽象構件
interface Component{
public void add(Component c);
public void operation();
}
//樹葉構件
class Leaf implements Component{
protected String name;
public Leaf(String name){
this.name=name;
}
@Override
public void add(Component c) {
// TODO Auto-generated method stub
}
@Override
public void operation(){
System.out.println("數葉"+name+":被訪問");
}
}
//樹枝構件
class Branch implements Component{
public ArrayList<Component> children=new ArrayList<Component>();
@Override
public void add(Component c) {
// TODO Auto-generated method stub
children.add(c);
}
@Override
public void operation(){
for(Object o:children){
((Component) o).operation();
}
}
}
@Test
public void CompositeTest(){
//三層結構,c0樹枝包含c1樹枝,c0和c1又各自包含自己的樹葉
Component c0=new Branch();
Component c1=new Branch();
Component leaf1=new Leaf("1:");
Component leaf2=new Leaf("2:");
Component leaf3=new Leaf("3:");
c0.add(leaf1);
c1.add(leaf2);
c1.add(leaf3);
c0.add(c1);
c0.operation();
}
總結:三層結構,c0樹枝包含c1樹枝,c0和c1又各自包含自己的樹葉.
- 享元模式
儘可能的讓用戶複用已經有的對象,從而避免造成反覆創建對象的資源浪費。首先會想到數據庫連接池和string常量池,延伸一下,幾乎所有和緩存有關的代碼,多少都會用到享元模式。享元模式對象的屬性可以分爲內部狀態和外部狀態,。內部狀態是指不會隨環境而改變的值,比如說個人信息,外部狀態是指隨環境改變的值,不能進行共享的信息。比如某大學生選修的課程。
abstract class Flyweight{
//內部狀態
private String name;
private String age;
//外部狀態
private final String subject;
protected Flyweight(String subject){
this.subject=subject;
}
public String getSubject(){
return subject;
}
}
class RealFlyWeight extends Flyweight{
protected RealFlyWeight(String subject) {
super(subject);
// TODO Auto-generated constructor stub
System.out.println("get "+subject);
}
}
//定義一個緩衝池
class FlyweightFactory{
public static HashMap<String, Flyweight> pool=new HashMap<String, Flyweight>();
public static Flyweight getFlyweight(String subject){
Flyweight flyweight=null;
//如果池子中已經存在了這個學科對象就複用,存在就新建一個,並把這個新建的放到池子中。
if(pool.containsKey(subject)){
flyweight=pool.get(subject);
}else{
flyweight=new RealFlyWeight(subject);
pool.put(subject, flyweight);
}
return flyweight;
}
}
@Test
public void Flyweight(){//這裏注意junit4測試類只能由public void修飾,且不能帶參數
FlyweightFactory ff=new FlyweightFactory();
System.out.println(ff.pool.size());//0
Flyweight flyweight=ff.getFlyweight("數學");
System.out.println(flyweight.getSubject());//get 數學
Flyweight flyweight2=ff.getFlyweight("數學");//數學
System.out.println(flyweight==flyweight2);//true
Flyweight flyweight3=ff.getFlyweight("語文");//get 語文
System.out.println(flyweight==flyweight3);//false
System.out.println(ff.pool.size());//2
}
總結,例子中介紹了,在緩衝池中創建對象,如果對象已經存在就複用,是同一個對象哦,不是克隆。不存在就新建一個,並放到池子中。
行爲型模式
創建了對象,對象之間有了結構關係,就要看下怎麼更優雅的相互作用了。
- 策略模式
定義一組算法,將每個算法都封裝起來,並使他們可以相互轉換。可以說是一組算法的封裝,根據客戶端給出的不同要求,進行不同的運算。
interface Strategy{
public int doOperation(int num,int num2);
}
class Add implements Strategy{
@Override
public int doOperation(int num, int num2) {
// TODO Auto-generated method stub
return num+num2;
}
}
class Substract implements Strategy{
@Override
public int doOperation(int num, int num2) {
// TODO Auto-generated method stub
return num-num2;
}
}
//環境類
class Context{
private Strategy strategy;
public Context(Strategy strategy){
this.strategy=strategy;
}
public int executeStrategy(int num1,int num2){
return strategy.doOperation(num1, num2);
}
}
@Test
public void Strategy(){
Context context=new Context(new Add());
System.out.println(context.executeStrategy(5, 2));//7
context=new Context(new Substract());
System.out.println(context.executeStrategy(5, 2));//3
Context context2=new Context(new Substract());
System.out.println(context2.executeStrategy(5, 2));//3
}
總結,在測試類中,根據內部環境的變化可進行不同計算,但還是同一個context。可以看出,這也是通過抽象接口Strategy和目標類context結合使用的案例,沒有涉及到繼承。
- 觀察者模式
指多個對象間存在一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。這種模式有時又被叫做發佈-訂閱者模式,沒錯就是在mq中的應用。降低了發佈者於訂閱者的耦合。
下面例子中介紹了被觀察者監考老師發佈了考試結束通知,訂閱者所有學生都上交試卷。
class Student{
private String name;
public Student(String name){
this.name=name;
}
public void dosomething(){
System.out.println(name+" 交卷了。");
}
}
class Teacher{
private Set<Student> students=new HashSet<Student>();
public void addStudent(Student s){
students.add(s);
}
public void donotify(){
for(Student student:students){
student.dosomething();
}
}
}
@Test
public void ObserverTest(){
Teacher teacher=new Teacher();
Student student=new Student("xiaoming");
Student student2=new Student("daliang");
teacher.addStudent(student);
teacher.addStudent(student2);
teacher.donotify();//daliang 交卷了。//xiaoming 交卷了。
}
- 責任鏈模式
爲了避免請求發送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一個對象記住其下一個對象的引用而連城一條鏈;當有請求發生時,可將請求沿着這條鏈傳遞下去。
//抽象處理者角色
abstract class Handler{
private Handler next;
public void setNext(Handler next){
this.next=next;
}
public Handler getNext(){
return next;
}
public abstract void handleRequest(String request);
}
//具體處理者
class ConcreteHandler extends Handler{
@Override
public void handleRequest(String request) {
// TODO Auto-generated method stub
if(request.equals("one")){
System.out.println("具體處理者1負責處理該請求!");
}else{
if(getNext()!=null){
getNext().handleRequest(request);
}else {
System.out.println("沒有人處理該請求!");
}
}
}
}
//具體處理者
class ConcreteHandler2 extends Handler{
@Override
public void handleRequest(String request) {
// TODO Auto-generated method stub
if(request.equals("two")){
System.out.println("具體處理者2負責處理該請求!");
}else{
if(getNext()!=null){
getNext().handleRequest(request);
}else {
System.out.println("沒有人處理該請求!");
}
}
}
}
@Test
public void ResponsibilityTest(){
Handler handler=new ConcreteHandler();
Handler handler2=new ConcreteHandler2();
handler.setNext(handler2);//這一步設置了具體處理者流程的前後關係,如果handle1不能處理就交給handle2
handler.handleRequest("two");//具體處理者2負責處理該請求!
}
總結,這個有點像我之前工作中用到過得activity工作流模式,也有些像OA系統,總而言之是個處理流程鏈。
- 模板方式模式
一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。springboot爲用戶封裝了很多繼承代碼,都用了模板模式,例如那一堆的XXXtemplate.
abstract class DBTemplate{
abstract void open();
abstract void select();
abstract void close();
public final void selectTemplate(){
open();
select();
close();
}
}
class MysqlDB extends DBTemplate{
@Override
void open() {
// TODO Auto-generated method stub
System.out.println("Mysql open...");
}
@Override
void select() {
// TODO Auto-generated method stub
System.out.println("Mysql select...");
}
@Override
void close() {
// TODO Auto-generated method stub
System.out.println("Mysql close...");
}
}
@Test
public void TemplateTest(){
DBTemplate template=new MysqlDB();
template.selectTemplate();
}
總結,這個案例整體感覺就是簡單的繼承使用。模板類中定了可變和不可變部分,不可變部分又包含可變部分。不可變的是流程的步驟,可變的是每個步驟的具體內容。就像我們使用文件流的時候,有時經常忘了寫關閉流管道的代碼,試想我們使用了模板後,它會強制提醒你或自動爲你關閉管道流。
- 狀態模式
對有狀態的對象,把複雜的判斷邏輯提取到不同的狀態對象中,允許狀態對象在其內部狀態發生改變時改變其行爲。簡而言之。就是一個對象有不同的狀態,根據狀態的不同,可能有不同的行爲。
//抽象狀態類
interface State{
public void doAction(Context2 context);
}
//具體狀態1
class StartState implements State{
@Override
public void doAction(Context2 context) {
// TODO Auto-generated method stub
System.out.println("player is in start state");
context.setState(this);
}
public String toString(){
return "start state";
}
}
//具體狀態2
class StopState implements State{
@Override
public void doAction(Context2 context) {
// TODO Auto-generated method stub
System.out.println("plauer is in stop state");
context.setState(this);
}
public String toString(){
return "stop state";
}
}
//環境類
class Context2{
private State state;
public Context2(){
state=null;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
@Test
public void StateTest(){
Context2 context2=new Context2();
StartState startState=new StartState();
//狀態作用在環境上,環境的狀態發生了改變
startState.doAction(context2);//player is in start state
System.out.println(context2.getState().toString());//start state
StopState stopState=new StopState();
//狀態作用在環境上,環境的狀態發生了改變
stopState.doAction(context2);//plauer is in stop state
System.out.println(context2.getState().toString());//stop state
}
總結,不同的狀態作用在環境上,使環境的狀態發生了改變。不同的狀態都實現了同一接口,各自的狀態也各有不同。至於環境只需添加總的接口即可。
- 迭代器模式
提供了一個對象來順序訪問聚合對象中的一系列數據,而不暴露聚合對象的內部表示。迭代器模式是一種對象行爲型模式。java中的iterator就是迭代器。
下面寫個迭代器例子,其實就是迭代器源碼了。
//抽象聚合
interface Aggregate{
public void add(Object object);
public void remove(Object object);
public Iterator getIterator();
}
//具體聚合
class ConcreteAggregate implements Aggregate{
private List<Object> list=new ArrayList<Object>();
@Override
public void add(Object object) {
// TODO Auto-generated method stub
list.add(object);
}
@Override
public void remove(Object object) {
// TODO Auto-generated method stub
list.remove(object);
}
@Override
public Iterator getIterator() {
// TODO Auto-generated method stub
return new ConcreteIterator(list);
}
}
//抽象迭代器
interface Iterator{
Object first();
Object next();
boolean hasNext();
}
//具體迭代器
class ConcreteIterator implements Iterator{
private List<Object> list=new ArrayList<Object>();
private int index=-1;
public ConcreteIterator(List<Object> list) {
// TODO Auto-generated constructor stub
this.list=list;
}
@Override
public Object first() {
// TODO Auto-generated method stub
index=0;
Object object=list.get(index);
return object;
}
@Override
public Object next() {
// TODO Auto-generated method stub
Object object=null;
if(this.hasNext()){
object=list.get(++index);
}
return object;
}
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if(index<list.size()-1){
return true;
}else{
return false;
}
}
}
@Test
public void IteratorTest(){
Aggregate aggregate=new ConcreteAggregate();
aggregate.add("num1");
aggregate.add("num2");
aggregate.add("num3");
System.out.println("聚合的內容有:");
Iterator iterator=aggregate.getIterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
聚合的內容有:
num1
num2
num3
- 命令模式
是將請求的命令包裹在對象中,並傳遞給對象,調用對象尋找到處理該命令的合適的對象。簡而言之就是不同請求都封裝成一個對象,不同的請求調用不同的執行者。
//調用者
class Invoker{
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void setCommand(Command command){
this.command=command;
}
public void call(){
command.exectue();
}
}
//執行者
class Receiver{
public void doSomething(){
System.out.println("Receiver幹活了");
}
}
abstract class Command{
public abstract void exectue();
}
class ConcreteCommand extends Command{
private Receiver receiver;
//創建構造函數快捷鍵 alt+shift+s +o
public ConcreteCommand() {
super();
receiver=new Receiver();
}
@Override
public void exectue() {
// TODO Auto-generated method stub
this.receiver.doSomething();
}
}
@Test
public void commandTest(){
Command command=new ConcreteCommand();
Invoker invoker=new Invoker(command);
invoker.call();
}
總結,這裏調用者Invoker 和執行者Receiver是分開的,也是通過Command命令類分開的,並且在測試類中沒有暴露出接受者。這個模式有點像適配器和代理模式。
- 備忘錄模式 又叫快照模式
相當於做一個快照,在不破壞對象本身結構的情況下,記錄對象的一個狀態,合適的時候可以恢復到這種狀態。數據庫做事務回滾的時候就用到了這種方式。
//備忘錄
class Memento{
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
//發起人
class Originator{
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento createMemento(){
return new Memento(state);
}
public void restoreMementor(Memento m){
this.setState(m.getState());
}
}
//管理者
class Caretaker{
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
@Test
public void MementoTest(){
Originator originator=new Originator();
Caretaker caretaker=new Caretaker();
originator.setState("S0");
System.out.println("初始狀態:"+originator.getState());
//管理者中也保存這個狀態
caretaker.setMemento(originator.createMemento());
//更改狀態
originator.setState("S1");
System.out.println("更改後的狀態:"+originator.getState());
originator.restoreMementor(caretaker.getMemento());
System.out.println("恢復狀態:"+originator.getState());
}
總結:可見對於原始對象,也就是要備份的對象Originator,需要在備忘錄類Memento 中也複製一份Originator。當需要恢復的時候再把備忘錄中的對象賦值回來。
- 訪問者模式
有些集合對象中存在多種元素。在不同外界訪問這些對象中的多種元素時,也會有不同的結果。可以理解成“多對多”的形式。就像公園中存在多個景點,也存在多個遊客,可見是多個遊客對應多個景點,那麼不同的遊客對同一景點的評價可能不同。
訪問者模式是行爲模式中最複雜的一種,訪問者模式處理這種多對多的思想就是:將複雜的對象中的各元素分別做操作,細分化處理,並賦予這些元素多種訪問方式。這是典型的將數據操作和數據結構的分離的思想。
//抽象訪問者
interface Visitor{
void visit(AElement aElement);
void visit(BElement bElement);
}
//訪問者A類
class AVisitor implements Visitor{
@Override
public void visit(AElement aElement) {
// TODO Auto-generated method stub
System.out.println("訪問者A訪問A元素"+aElement.operationA());
}
@Override
public void visit(BElement bElement) {
// TODO Auto-generated method stub
System.out.println("訪問者A訪問A元素"+bElement.operationB());
}
}
class BVisitor implements Visitor{
@Override
public void visit(AElement aElement) {
// TODO Auto-generated method stub
System.out.println("訪問者B訪問A元素"+aElement.operationA());
}
@Override
public void visit(BElement bElement) {
// TODO Auto-generated method stub
System.out.println("訪問者B訪問B元素"+bElement.operationB());
}
}
//抽象元素
interface Element{
void accept(Visitor visitor);
}
//子元素1
class AElement implements Element{
@Override
public void accept(Visitor visitor) {
// TODO Auto-generated method stub
visitor.visit(this);
}
public String operationA(){
return "operation A...";
}
}
//子元素2
class BElement implements Element{
@Override
public void accept(Visitor visitor) {
// TODO Auto-generated method stub
visitor.visit(this);
}
public String operationB(){
return "operation B...";
}
}
//對象結構類角色
class ObjectStructure{
private List<Element> list=new ArrayList<Element>();
public void accept(Visitor visitor){
java.util.Iterator<Element> iterator=list.iterator();
while(iterator.hasNext()){
iterator.next().accept(visitor);
}
}
public void add(Element element){
list.add(element);
}
}
@Test
public void visitorTest(){
ObjectStructure objectStructure=new ObjectStructure();
objectStructure.add(new AElement());
objectStructure.add(new BElement());;
Visitor visitor=new AVisitor();
objectStructure.accept(visitor);//訪問者A訪問A元素operation A... 訪問者A訪問A元素operation B...
visitor=new BVisitor();
objectStructure.accept(visitor);//訪問者B訪問A元素operation A... 訪問者B訪問B元素operation B...
}
總結:這個模式確實很難,理解起來很費勁。各訪問者與各元素間你中有我,我中有你,在官方說法中也說了者違背了“開閉原則”,破壞了封裝性。這裏我們就其負責的邏輯模式討論,首先把多個元素放到集合中,創建一個訪問者,這個訪問者是被各元素允許訪問的,最後在各元素中通過接口轉接到各訪問者,訪問者們“發表自己的看法”。
22. 中介者模式
就是要訪問的對象不方便直接訪問的時候,就需要一箇中介,這裏叫做訪問者,我們將自己的引用交給訪問者訪問該對象。比如,化學課,我們想看到微觀的細胞結構,需要藉助顯微鏡。編程中,比如QQ,介於交流者兩方間的QQ服務器就是中間件。
//聊天室中介
class ChatRoom{
public static void showMessage(User2 user,String message){
System.out.println(user.name+" "+message);
}
}
class User2{
public String name;
public User2(String name) {
this.name = name;
}
public void sendMessage(String message){
ChatRoom.showMessage(this, message);
}
public static void main(String[] args) {
User2 robert=new User2("Robert");
User2 john=new User2("John");
robert.sendMessage("Hi John");
john.sendMessage("hello Robert");
}
}
Robert Hi John
John hello Robert
23,解釋器模式
我個人關鍵這個沒啥存在的意義。借用官方的定義:給分析對象定一個語言,並定義該語言的文法表示,再設計一個解釋器來解釋語言中的句子。也就是說,用編譯的語言的方式來分析應用中的實例。
//抽象表達式類
interface Expression{
public boolean interpret(String info);
}
//終結符表達式類
class TermianlExpression implements Expression{
private Set<String> set=new HashSet<String>();
public TermianlExpression(String[] data) {
for(int i=0;i<data.length;i++){
set.add(data[i]);
}
}
@Override
public boolean interpret(String info) {
// TODO Auto-generated method stub
if(set.contains(info)){
return true;
}
return false;
}
}
//非終結符表達式
class AndExpression implements Expression{
private Expression city=null;
private Expression person=null;
public AndExpression(Expression city, Expression person) {
this.city = city;
this.person = person;
}
@Override
public boolean interpret(String info) {
// TODO Auto-generated method stub
String[] s=info.split("的");
return city.interpret(s[0]) && person.interpret(s[1]);
}
}
//環境類
class Context3{
private String[] citys={"韶關","廣州"};
private String[] persons={"老人","婦女","兒童"};
private Expression cityPerson;
public Context3() {
Expression city=new TermianlExpression(citys);
Expression person=new TermianlExpression(persons);
cityPerson=new AndExpression(city, person);
}
public void freeRide(String info){
boolean ok=cityPerson.interpret(info);
if(ok){
System.out.println("您是"+info+",您本次乘車免費!");
}else{
System.out.println("您是"+info+",您本次乘車不免費!");
}
}
}
@Test
public void InterpreterTest(){
Context3 bus=new Context3();
bus.freeRide("韶關的老人");
bus.freeRide("韶關的年輕人");
bus.freeRide("廣州的婦女");
bus.freeRide("廣州的兒童");
}
您是韶關的老人,您本次乘車免費!
您是韶關的年輕人,您本次乘車不免費!
您是廣州的婦女,您本次乘車免費!
您是廣州的兒童,您本次乘車免費!
總結,上面代碼的終結符啥的看着很高大上,其實就是一個判斷,給我們輸入的句子做分割判斷,得出我們預想的結果。我們輸入的語言進入到環境類中,經過終結類與非終結類的判斷,得出乘車是否收費。
總結
23中模式介紹完了,可以看出就是對java幾大特性封裝,繼承,接口,抽象的花樣應用。