深入浅出设计模式(1)

来源:互联网 发布:神知世界txt下载 编辑:程序博客网 时间:2024/05/21 14:56

故学然后知不足,教然后知困。知不足,然后能自反也;知困,然后能自强也,故曰:教学相长也。——礼记学记

OOD(面向对象设计)如何才算是好的呢?我们可以用GRASP模式原则指导。
GRASP(General Responsibility Assignment Software Patterns)

  • General / 通用的、抽象的、广泛应用的
  • Responsibility/ 责任、义务、职责
  • Assignment /分配责任于一个模块或类
  • Software /计算机代码、软件
  • Patterns/规律、模板、抽象或模式

GRASP系列模式分为9种,详述这9种模式。

  • Information Expert 信息专家
    最基本的原则,如果某个类在某方面具有完整信息,足以实现某个责任,就将这个责任分配给这个类。
    比如说程序要检验两个商品是否是同一个商品,而商品编码在建模中描述为ProductID,哪个类具有这一项呢?就是Item类,根据信息专家原则,Item类应该包括检测责任。
  • Creator 创造者
    以下情况,类A应该具有创建类B的责任:
    (1) A是B的聚合
    (2) A是B的容器
    (3) A有初始化B的数据
    (4) A记录B的实例
    (5) A频繁使用B

  • Low Coupling 低耦合
    (1) 低耦合使得一个类的修改对其他类的影响范围有所降低
    (2) 系统维护更容易
    (3) 使得类更容易理解,因为类会变得简单、专业、高内聚
    不要对陌生人说话
    不要连接两个不需要通信的对象
    两个模块中的内部类间连接是一个大错误

  • High Cohesion 高内聚
    使内聚保持最高,目的是为了提高设计类的重用性,并且控制类设计的复杂程度。
    含义就是努力的使得分出来的类具有独立的责任。
    比如一个类单独处理很多不同模块的事务,比如一个类既处理对数据的存取,又处理用户接口操作或图形处理,这种跨模块的设计就是非常低的内聚。
    低类聚将会导致:
    (1) 很难被理解和维护
    (2) 难以实现类的重用
    (3) 系统变得脆弱,不断需要修改

  • Controller 控制器
    系统事件的接收和处理通常由一个高级类来代替,一个子系统会有很多控制器类,分别处理不同的事务。
    人们通常将接受和处理系统事件的职责分配给以下类,这些类也是控制器类:
    (1) 能全面代表系统、设备或子系统的类
    (2) 可以代表系统事件发生时用例发生情景的类
    (3) 代表某些卷入真是世界应用中活动的类,例如人物角色控制器类。
    比如银行系统中,用一个控制器处理所有银行事务,即类TransactionController。包括
    deposit(), withdraw(), payBill(), transfer();
    大家常用的MVC三层架构强调了控制模式的应用。

  • Polymorphism 多态
    如果相关的行为只是由于种类不同,可以分配相关责任给指定的种类。
    比如定义一个抽象形状类shape,矩形、圆形、椭圆形都重写shape类的draw方法,使用这种多态机制,将绘画的责任分配给指定的类。这样新的形状加入系统后,不会对其他类有任何影响。

  • Pure Fabrication 纯虚构
    实现高内聚和低耦合都是系统设计的目标,但是高内聚和低耦合是矛盾的。因为高内聚往往意味着类的数量增多,对象间要合作完成任务,使得耦合增高。
    要解决这个矛盾,可以应用纯虚构模式,它增加了高内聚的特征。抽象工厂模式是纯虚构模式的一种实际体现。

  • Indirection 间接
    避免对象间直接的耦合,可以将协调组件或服务的职责分配给中间对象,中间对象称为间接或中介对象。
    直接耦合两个系统往往会引起维护的困难,间接模式就是一个解决方案,引入中间对象可以使得两个子系统的重用性得到提高。比如人事系统中岗位及员工之间的关系,员工分配在岗位上,它们有连接。但是员工和岗位都是独立的,所以我们需要设计一个中间类——分配。
    这里写图片描述
  • Protected Variations 受保护变化
    找出预计有变化或不稳定的点,为这些变化的点创建稳定的接口,比如游戏开发商致力于开发可通用的游戏引擎,它是个外层包装模块,使用相同的游戏引擎可以开发出不同的游戏。
    受保护变化可以理解为开闭原则,一个软件实体应该对扩展开放,对修改关闭。

创建型设计模式

简单工厂模式

定义:
专门定义一个类来负责创建其他类的实例,被创建的实例具有共同的父类。又称为静态工厂方法(static factory method)。

public interface Mobile{ public void call();}public class Nokia implements Mobile{    public void call(){        System.out.println("nokia手机");    }}public class Motorola implements Mobile{    public void call(){        System.out.println("Motorola手机");    }}public class MobileFactory{    public Mobiel getMobile(String title) throws Exception{        if(title.equalsIgnoreCase("nokia")){            return new Nokia();        }        else if(title.equalsIgnoreCase("motorola")){            return new Motorola();        }        else{            throw new Exception("no such"+title+"mobile found");        }    }}public class SimpleFactoryClient{    static public void main(String argv[]){        try{            MobileFactory mf = new MobileFactory();            Mobile m;            m = mf.getMobile("nokia");            m.call();            m = mf.getMobile("motorola");            m.call();               }        catch(Exception e){            e.printStackTrace();        }    }}

优点:
外界可以从直接创建具体产品对象的尴尬局面中摆脱出来,不必管这些对象是如何创建以及如何组织的。
缺点:
全部创建逻辑都集中到一个工厂类,当系统中具体产品类不断增多时,可能会出现要求工厂类根据不同条件创建不同实例的需求。这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的扩展和维护也非常不利。

应用情景:
1,工厂类负责创建的对象比较少;2,客户只知道传入工厂类的参数,对于如何创建对象不关心。

工厂方法模式

定义:
工厂方法模式又叫虚拟构造器模式,或者多态工厂,类的创建型模式。父类负责定义创建对象的公共接口,子类负责生成具体的对象,将类的实例化操作延迟到子类中完成,既由子类来决定究竟应该实例化哪个类。

//手机接口public interface Mobile{    public void call();}//手机工厂接口public interface MobileFactory{    public Mobile produceMobile();}//摩托罗拉手机实现手机接口public class Motorola implements Mobile{    public void call(){        System.out.println("摩托罗拉手机");    }}//诺基亚手机实现手机接口public class Nokia implements Mobile{    public void call(){        System.out.println("诺基亚手机");    }}//摩托罗拉工厂实现生产手机方法public class MotorolaFactory implements MobileFactory{    public Mobile produceMobile(){        System.out.println("摩托罗拉工厂制造了");        return new Motorola();    }}//诺基亚工厂实现生产手机方法public class NokiaFactory implements MobileFactory{    public Mobile produceMobile(){        System.out.println("诺基亚工厂制造了");        return new Nokia();    }}//客户程序public class Client{    public static void main(String argv[]){        MobileFactory mf;        Mobile m;        mf = new MotorolaFactory();        m = mf.produceMobile();        m.call();        mf = new NokiaFactory();        m = mf.produceMobile();        m.call();    }}

优点:
客户只关心抽象产品和抽象工厂,完全不用理会返回的是哪一种具体产品,不用关心它是如何被具体工厂创建的。
(1)工厂角色和产品角色的多态性设计是工厂方法模式的关键,它能够使工厂可以自主确定创建何种产品对象,创建产品对象的细节封装在具体工厂内部。
(2)在系统中加入新产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端。也不用修改其他的具体工厂和具体产品,只要添加一个具体工厂和一个具体产品就可以了。

优秀的面向对象设计鼓励使用封装和委托,封装是通过抽象工厂来体现的,委托是通过抽象工厂将创建对象的责任完全交给具体工厂来体现。

应用情景
1. 类不知道自己要创建哪一个对象。
2. 类用它的子类来指定创建哪个对象。
3. 客户需要清楚创建了哪一个对象

抽象工厂模式

定义
提供一系列相关和相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。

抽象工厂模式与工厂方法模式最大的不同在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式针对的是多个产品等级结构。在抽象工程模式中,经常会用到产品族这一个概念。

//cpu接口public interface cpu{ public string getCPU();}//AMD类,实现cpu接口public class AMD implements CPU{    public String getCPU(){        return "Athlon XP 2800+";    }}//Intel类,实现cpu接口public class Intel implements CPU{    public String getCPU(){        return "奔腾4";    }}//硬盘接口public interface HardDisk{    public String getSize();}//Maxtor 实现硬盘接口public class Maxtor implements HardDisk{    public String getSize(){        return "MaXLIne Plus II 200G";    }}//定义WestDigit 实现硬盘接口public class WestDigit implements HardDisk{    publis String getSize(){        return "WD2500JD 250G";    }}//定义主板接口public interface MainBoard{    public void Attach(CPU cpu) throws Exception;}//主板微星,支持Intel的CPUpublic class MSI865PE implements MainBoard{    public void Attach(CPU cpu) throws Exception{        if(cpu.getClass().toString().endsWith("Intel")){            System.out.println("MSI865PE");        }        else{            throw new Exception("主板MSI765PE只能配Intel的CPU");        }    }}//主板微星,支持AMD的CPUpublic class MSIK7N2G implements MainBoard{    public void Attach(CPU cpu) throws Exception{        if (cpu.getClass().toString().endsWith("AMD")){            System.out.println("MSIK7N2G");        }        else{            throw new Exception("主板MSIK7N2G只能配AMD的CPU");        }    }}//定义抽象电脑工厂类public abstract class ComputerFactory{    CPU cpu;    HardDisk hd;    MainBoard mb;    public void main(){        try{            System.out.println(this.getClass().getName().toString());            System.out.println(cpu.getCpu());            System.out.println(hd.getSize());            mb.Attach(cpu);        }catch(Exception e){            System.err.println(e.getMessage());        }    }}//抽象电脑工厂类派生类,定义其返回的系列配件产品public class IBM extends ComputerFactory{    IBM(){        cpu = new Intel();        hd = new WestDigit();        mb = new MSI865PE();    }}//客户程序调用:public class Client{    public static void main(String argv[]){        IBM ibm = new IBM();        ibm.show();        Dell dell = new Dell();        dell.show();    }}

优势:
抽象工厂模式的主要优点是隔离了具体类的生成,是的客户不需要知道什么被创建了。由于这种隔离,更换一个具体工厂就变得相对容易,所有具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

建造者模式

定义
将一个复杂对象的构建与它的表示分离,使得同样构建过程可以创建不同的表示。建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不知道内部具体构建细节。
其中类与对象之间关系为:
Builder:抽象建造者 为创建一个Product对象各个部件指定抽象接口
ConcreteBuilder: 具体建造者,实现Builder接口,构造和装配产品各个部件;定义并明确它们所创建的表示;提供一个返回这个产品的接口。
Director: 指挥者,构建一个使用Builder接口的对象。
Product: 产品角色:被构建的复杂对象,具体建造者创建该产品的内部表示并定义它的装配过程;包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

为什么需要Director,Director类的作用是巨大的。
第一,它隔离了客户及生产过程,
第二,它负责控制产品的生成过程。

比如你要去买车子,选好车型、颜色、内外饰等,交给Director,Director告诉你去某车间取车就可以。

Builder模式与AbstractFactory模式的区别
建造者模式与抽象工厂模式很像,但是Builder模式返回完整的一个产品,而AbstractFactory返回一系列产品;
在抽象工厂模式中,客户采用AbstractFactory生成自己要用的对象。
而在建造者模式中,客户指导Builder类如何去生成对象,或是如何合成一些类来构成建造类,侧重于一步步构造一个复杂对象,然后将结果返回。

//定义House接口public interface House{    boolean GetBackyard();    long NoOfRooms();    String Description();}//房间类public class Room{    public String RoomName;}//定义Apartment公寓,有3间房间,没有后院public class Apartment implements House{    private boolean mblnBackyard;    private Vector Rooms;    public Apartment(){        Room room = new Room();        Rooms = new Vector();        room.RoomName = "Master Room";        Rooms.addElement(room);        room = new Room();        room.RoomName = "Second Bedroom";        Rooms.addElement(room);        room = new Room();        room.RoomName = "Living Room";        Rooms.addElement(room);        mblnBackyard = false;    }    public boolean GetBackyard(){        return mblnBackyard;    }    public long NoOfRooms(){        return Rooms.size();    }    public String Description(){        String strDescription;        strDescription="这是一间公寓,有"+Rooms.size()+"间房间\n";        strDescription=strDescription+"这间公寓没有后院";        for(int i=1;i<=Rooms.size();i++){            strDescription = strDescription + i +((Room)Rooms.elementAt(i-1)).RoomName;        }        return strDescription;    }}//定义单一家庭房屋public class SingleFamilyHome implements House{    private boolean mblnBackyard;    private Vector Rooms;    public SingleFamilyHome(){        Room room = new Room();        Rooms = new Vector();        room.RoomName = "master bedroom";        Rooms.addElement(room);        room = new Room();        room.RoomName = "second bedroom";        Rooms.addElement(room);        room = new Room();        room.RoomName = "third Room";        Rooms.addElement(room);        ...        mblnBackyard = true;    }    public boolean GetBackyard(){        return mblnBackyard;    }    public long NoOfRooms(){        return Rooms.size();    }    public String Description(){        String strDescription;        strDescription ="这是一间公寓,有"+Rooms.size()+"间房间";        for(int i=1;i<=Rooms.size();i++){            strDescription = strDescription + i + ((Room)Rooms.elementAt(i-1)).RoomName;        }        return strDescription;    }}//Director根据客户传入的mblnBackyard参数的值,创建不同的House:public class Director{    public House BuildHouse(boolean mblnBackyard){        if(mblnBackyard){            return new SingleFamilyHome();        }        else{            return new Apartment();        }    }}//客户程序通过调用Director的BuildHouse方法建造房屋,传入参数决定房屋类型public class Client{    public static void main(String argv[]){        Director director = new Director();        House house = director.BuildHouse(false);        System.out.println(house.Description());    }}

应用场景:
下面情景适合应用建造者模式:
(1) 创建复杂对象的算法是独立于它的组成部件及装配过程
(2) 构造的过程允许构造对象有不同的表现


参考资料:
深入浅出设计模式,清华大学出版社

1 0
原创粉丝点击