java设计模式--七大原则
2016-05-14 20:45:38
设计模式到底是五大、六大还是七大,一直傻傻分不清楚,网上总是有说那些原则可以归为一个,我纠结了半天,突然发现自己是舍本逐末了,只要清楚这些原则的设计思想,其他的理解自然是水到渠成。
1、开放-封闭原则
2、单一职责原则
3、里氏代换原则
4、依赖倒转原则
5、迪米特法则
6、接口隔离原则
7、合成/聚合复用原则
一、开放封闭原则(open-closed principle)
一个软件实体应当对外拓展开放,对修改关闭。
在设计一个模块时,应当使得这个模块可以在不被修改的前提下被扩展。也就是说,应当可以在不必修改源代码的情况下修改这个模块的行为。
设计的目的便在于面对需求的改变而保持系统的相对稳定,从而使得系统可以很容易的从一个版本升级到另一个版本。
例子:银行业务员

先前的模式:
每个银行业务员都要负责储户的付款、取款、转账等业务,当业务变更时,所有银行业务员的职责都要发生变化,业务少了自然能够轻松,业务多了所有人都要培训,拓展性不好,而通常来说,储户的需求是不断增加的。
/*
* 银行业务员
*/
public class BankWorker {
//负责存款业务
public void saving() {
System.out.println("进行存款操作");
} //负责取款业务
public void drawing() {
System.out.println("进行取款操作");
} //负责转账业务
public void zhuanzhang() {
System.out.println("进行转账操作");
} //负责基金的申购
public void jijin() {
System.out.println("进行基金申购操作");
}
}
public class MainClass {
    public static void main(String[] args) {
        BankWorker bankWorker = new BankWorker();
        //存款
        bankWorker.saving();
        //取款
        bankWorker.drawing();
        //转账
        bankWorker.zhuanzhang();
        //基金
        bankWorker.jijin();
    }
}
修改后的模式:

一个银行业务员专门负责一个业务,非常方便业务的增加,只要继续安排对应的银行业务员即可。
/*
* 银行业务员接口,是所有银行业务员的抽象父类。
*/
public interface BankWorker {
public void operation();
}
/*
* 负责取款业务的业务员
*/
public class DrawingBankWorker implements BankWorker{
public void operation() {
System.out.println("进行取款操作");
}
}
/*
* 负责存款业务的业务员
*/
public class SavingBankWorker implements BankWorker {
public void operation() {
System.out.println("进行存款操作");
}
}
/*
* 负责转账业务的业务员
*/
public class ZhuanZhangBankWorker implements BankWorker {
public void operation() {
System.out.println("进行转账操作");
}
}
/*
* 负责基金业务的业务员
*/
public class JiJinBankWorker implements BankWorker {
public void operation() {
System.out.println("进行基金申购操作");
}
}
public class MainClass {
    public static void main(String[] args) {
        BankWorker bankWorker = new SavingBankWorker();
        bankWorker.operation();
        BankWorker bankWorker2 = new DrawingBankWorker();
        bankWorker2.operation();
        BankWorker bankWorker3 = new ZhuanZhangBankWorker();
        bankWorker3.operation();
        BankWorker bankWorker4 = new JiJinBankWorker();
        bankWorker4.operation();
    }
}
当需求变化时,不是修改原来代码,而是通过添加代码实现。这样,程序的扩展性好,也易于维护和升级,而具体想要达到这样的效果,需要使用接口和抽象类:将那些无法封闭的变化抽象出来,进行隔离,允许拓展。
开放封闭原则的优越性:
1.通过扩展已有的软件系统,可以提供新的行为,以满足对软件的新需求,使变化中的软件有一定的适应性和灵活性。
2.已有的软件模块,特别是最重要的抽象模块不能再修改,这就使变化中的软件系统有一定的稳定性和延续性。
二、单一职责原则(single resposibility principle)
就一个类而言,应该仅有一个引起它变化的原因。
每一个职责都是一个变化的轴线,当需求变化时会反映为类的职责的变化。如果一个类承担的职责多于一个,那么引起它变化的原因就有多个。一个职责的变化甚至可能会削弱或者抑制类完成其他职责的能力,从而导致脆弱的设计。像山寨手机,有很多功能,如拍照、摄像、手机游戏、网络摄像头、GPS、炒股等等。虽然功能多,但是每一个功能都不强。
如果一个类里面有各种各样的代码,什么算法、数据库访问之类的都写在一起,这样无论任何需求要来,都需要更改这个类,维护麻烦,复用不可能,也缺乏灵活性。
例子:接受客户端输入并提交到数据库。
原有设计:
一个类负责接受客户端输入,对客户端输入进行校验,连接数据库,并提交数据到数据库。
import java.util.Scanner;
public class SaveToDB {
    private String username;
    private String upassword;
    //获得客户端输入
    public void shuru() {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名");
        username = scanner.nextLine();
        System.out.println("请输入密码");
        upassword = scanner.nextLine();
    }
    //进行数据校验
    public boolean validate() {
        if(username == null || "".equals(username.trim())) {
    //trim()方法删除字符串起始和结尾的空格,返回调用字符串对象的一个副本
            System.out.println("用户名不能为空");
            return false;
        }
        if(upassword == null || "".equals(upassword.trim())) {
            System.out.println("密码不能为空");
            return false;
        }
        return true;
    }
    //连接数据库
    public void getConnection() {
        System.out.println("获得数据库连接");
    }
    //进行数据操作
    public void save() {
        System.out.println("将" + username + "保存到了数据库");
        System.out.println("将" + upassword + "保存到了数据库");
    }
}
import java.util.Scanner;
public class MainClass {
    public static void main(String[] args) {
        //接受客户端的输入
        SaveToDB std = new SaveToDB();
        std.shuru();
        if(std.validate()) {
            std.getConnection();
            std.save();
        }
    }
}
现有设计:
一个功能也就是一个职责由一个类来负责。
import java.util.Scanner;
public class Input {
    private String username;
    private String upassword;
    //获得客户端输入
    public void shuru() {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名");
        username = scanner.nextLine();
        System.out.println("请输入密码");
        upassword = scanner.nextLine();
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getUpassword() {
        return upassword;
    }
    public void setUpassword(String upassword) {
        this.upassword = upassword;
    }
}
public class Validator {
    //进行数据校验
    public boolean validate(String username, String upassword) {
        if(username == null || "".equals(username.trim())) {
            System.out.println("用户名不能为空");
            return false;
        }
        if(upassword == null || "".equals(upassword.trim())) {
            System.out.println("密码不能为空");
            return false;
        }
        return true;
    }
}
public class DBManager {
    //连接数据库
    public static void getConnection() {
        System.out.println("获得数据库连接");
    }
}
public class DAOImp {
    //进行数据操作
    public void save(String username,String upassword) {
        System.out.println("将" + username + "保存到了数据库");
        System.out.println("将" + upassword + "保存到了数据库");
    }
}
public class MainClass {
    public static void main(String[] args) {
        Input input = new Input();
        input.shuru();
        Validator validator = new Validator();
        if(validator.validate(input.getUsername(), input.getUpassword())){
            DBManager.getConnection();
            DAOImp dao = new DAOImp();
            dao.save(input.getUsername(), input.getUpassword());
        }
    }
}
三、里氏代换原则(Liskov substitution principle)
里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化,而里氏代换原则是对实现抽象化的具体步骤的规范。
LSP是继承复用的基石,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且察觉不出基类和子类对象的区别。也就是说,在软件里面,把基类替换成它的子类,程序的行为没有变化,软件单位的功能不会受到影响。这样基类才能真正被复用,而子类也能够在基类的基础上增加新的行为。但是,反过来的代换不成立,也就是说,一个软件实体如果使用的是一个子类的话,那么它不能适用于其基类。
例子1:人和男人
public class Person {
    public void display() {
        System.out.println("this is a person");
    }
}
public class Man extends Person {
    public void display() {
        System.out.println("this is a man");
    }
}
public class MainClass {
    public static void main(String[] args) {
        Person person = new Person();
        display(person);
        Man man = new Man();
        display(man);
    }
    public static void display(Person person) {
        person.display();
    }
}
显然,方法display既适用于父类Person,又适用于子类Man。但如果将方法display(Person person)修改为以下形式,只能接受子类Man。
public static void display(Man person) {
        person.display();
    }
例子2:长方形和正方形

长方形有两个属性:width height;正方形有一个属性:side。正方形不是长方形的子类,可以将他们看成两个独立的类。
如果我们强制正方形继承长方形,

public class ChangFX {
    private long width;
    private long height;
    public long getWidth() {
        return width;
    }
    public void setWidth(long width) {
        this.width = width;
    }
    public long getHeight() {
        return height;
    }
    public void setHeight(long height) {
        this.height = height;
    }
}
public class ZhengFX implements ChangFX{
    private long side;
    public long getHeight() {
        return this.getSide();
    }
    public long getWidth() {
        return this.getSide();
    }
    public void setHeight(long height) {
        this.setSide(height);
    }
    public void setWidth(long width) {
        this.setSide(width);
    }
    public long getSide() {
        return side;
    }
    public void setSide(long side) {
        this.side = side;
    }
}
public class MainClass {
    public static void main(String[] args) {
        ChangFX changfx = new ChangFX();
        changfx.setHeight(10);
        changfx.setWidth(20);
        test(changfx);
        ZhengFX zhengfx = new ZhengFX();
        zhengfx.setHeight(10);
        zhengfx.setWidth(20);
        test(zhengfx);
    }
    public static void test(ChangFX changfx) {
        System.out.println(changfx.getHeight());
        System.out.println(changfx.getWidth());
    }
}
这样骗过了java编译器,但是上述定义的长方形和正方形并不是真的符合里氏代换原则,不妨在MainClass中增加下面方法,程序会陷入死循环中。
public static void resize(Changfx changfx) {
        while(changfx.getHeight() <= changfx.getWidth()) {
            changfx.setHeight(changfx.getHeight() + 1);
            test(changfx);
        }
}
如果我们构造一个抽象的四边形类,里面只有取width和height的抽象方法,没有对width和height赋值的方法,让长方形和正方形都是四边形的子类,这样LSP就不会被破坏。
public interface Sibianxing {
    public long getWidth();
    public long getHeight();
}
public class ChangFX implements Sibianxing{
    private long width;
    private long height;
    public long getWidth() {
        return width;
    }
    public void setWidth(long width) {
        this.width = width;
    }
    public long getHeight() {
        return height;
    }
    public void setHeight(long height) {
        this.height = height;
    }
}
public class ZhengFX implements Sibianxing{
    private long side;
    public long getHeight() {
        return this.getSide();
    }
    public long getWidth() {
        return this.getSide();
    }
    public void setHeight(long height) {
        this.setSide(height);
    }
    public void setWidth(long width) {
        this.setSide(width);
    }
    public long getSide() {
        return side;
    }
    public void setSide(long side) {
        this.side = side;
    }
}
public class MainClass {
    public static void main(String[] args) {
        ChangFX changfx = new ChangFX();
        changfx.setHeight(10);
        changfx.setWidth(20);
        test(changfx);
        ZhengFX zhengfx = new ZhengFX();
        zhengfx.setHeight(10);
        test(zhengfx);
    }
    public static void test(Sibianxing sibianxing) {
        System.out.println(sibianxing.getHeight());
        System.out.println(sibianxing.getWidth());
    }
}
例子3:鸟和企鹅

public interface Bird {
    public void fly();
}
public class Laoying implements Bird {
    public void fly() {
        System.out.println("老鹰在飞");
    }
}
public class Maque implements Bird {
    public void fly() {
        System.out.println("麻雀在飞");
    }
}
public class Qie{
    public void fly(){
    //企鹅不会飞
    }
}
public class MainClass {
    public static void main(String[] args) {
        fly(new Laoying());
        fly(new Maque());
        fly(new Qie());
    }
    public static void fly(Bird bird) {
        bird.fly();
    }
}
可以看到企鹅虽然属于鸟类,但并不会飞,不必强求它实现上述定义的Bird接口。这里的企鹅和鸟之间不存在里氏代换原则。
四、依赖倒转原则(dependency inversion principle)
传统的过程式设计倾向于使高层次的模块依赖于低层次的模块,抽象层依赖于具体层

倒转后:

依赖倒转是开-闭原则的基础,具体内容是:
1.抽象不应该依赖于细节(具体),细节(具体)应该依赖于抽象。
2.高层模块不依赖底层模块,两者都依赖抽象。
例子: 组装电脑

/*
* 主板抽象类
*/
public abstract class MainBoard {
public abstract void doSomething();
}
/*
* 华硕主板
*/
public class HuaShuoMainBoard extends MainBoard{ public void doSomething() {
System.out.println("this is huashuoMainBoard");
}
}
/*
* 微星主板
*/
public class WeiXingMainBoard extends MainBoard { public void doSomething() {
System.out.println("this is weixingMainBoard");
} }
/*
* 硬盘的抽象类
*/
public abstract class HardDisk {
public abstract void doSomething();
}
/*
* 希捷硬盘
*/
public class XiJieHardDisk extends HardDisk { public void doSomething() {
System.out.println("this is xijieHardDisk");
}
}
/*
* 西数硬盘
*/
public class XiShuHardDisk extends HardDisk { public void doSomething() {
System.out.println("this is xishuHardDisk");
}
}
/*
* 内存的抽象类
*/
public abstract class Memory {
public abstract void doSomething();
}
/*
* 金士顿内存
*/
public class JinShiDunMemory extends Memory { public void doSomething() {
System.out.println("this is jinshidunMemory");
}
}
/*
* 金邦内存
*/
public class JinBangMemory extends Memory { public void doSomething() {
System.out.println("this is jinbangMemory");
}
}
/*
* 电脑
*/
public class Computer {
private MainBoard mainBoard;
private Memory memory;
private HardDisk harddisk; public MainBoard getMainBoard() {
return mainBoard;
} public void setMainBoard(MainBoard mainBoard) {
this.mainBoard = mainBoard;
} public Memory getMemory() {
return memory;
} public void setMemory(Memory memory) {
this.memory = memory;
} public HardDisk getHarddisk() {
return harddisk;
} public void setHarddisk(HardDisk harddisk) {
this.harddisk = harddisk;
} public void display() {
mainBoard.doSomething();
memory.doSomething();
harddisk.doSomething();
}
}
public class MainClass {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.setMainBoard(new HuaShuoMainBoard());
        computer.setMemory(new JinShiDunMemory());
        computer.setHarddisk(new XiJieHardDisk());
        computer.display();
        System.out.println("-------------");
        computer.setMainBoard(new WeiXingMainBoard());
        computer.display();
    }
}
如果将Computer类改为下列形式,主要是违背了第二条,高层次模块依赖于低层次模块。
public class Computer {
    private HuaShuoMainBoard mainBoard;
    private JinShiDunMemory memory;
    private XiJieHardDisk harddisk;
    public HuaShuoMainBoard getMainBoard() {
        return mainBoard;
    }
    public void setMainBoard(HuaShuoMainBoard mainBoard) {
        this.mainBoard = mainBoard;
    }
    public JinShiDunMemory getMemory() {
        return memory;
    }
    public void setMemory(JinShiDunMemory memory) {
        this.memory = memory;
    }
    public XiJieHardHardDisk getHarddisk() {
        return harddisk;
    }
    public void setHarddisk(XiJieHardHardDisk harddisk) {
        this.harddisk = harddisk;
    }
    public void display() {
        mainBoard.doSomething();
        memory.doSomething();
        harddisk.doSomething();
    }
}
在工厂方法模式、模板方法模式、迭代子模式中都有明显的依赖倒转。
五、迪米特法则(law of Demeter)
迪米特法则最初是用来作为面向对象的系统设计风格的一种法则,于1987年秋天由lan holland在美国东北大学为一个叫做迪米特的项目设计提出的。 迪米特法则(Law of Demeter )又叫做最少知识原则,也就是说,一个对象应当对其他对象尽可能少的了解,应可能少的与其他对象发生相互作用,使得系统功能模块相对独立。在类的结构设计上,每一个类都应当尽量降低成员的访问权限。
迪米特法则: 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一类的某一个方法的话,可以通过第三者转发这个调用。其根本思想是强调了类之间的耦合性。类之间的耦合越弱,越有利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成波及。
例子:不要和陌生人说话

这里,“某人”没有通过“朋友”,而是直接和“陌生人”联系了
public class SomeOne {
    public void play(Friend friend){
        System.out.println("someone play");
        friend.play();
    }
    public void play(Stranger stranger) {
        System.out.println("someone play");
        stranger.play();
    }
}
public class Friend {
    public void play(){
        System.out.println("friends paly");
    }
}
public class Stranger {
    public void play(){
        System.out.println("stranger play");
    }
}
public class MainClass {
    public static void main(String[] args) {
        SomeOne zhangsan = new SomeOne();
        zhangsan.play(new Friend());
        zhangsan.play(new Stranger());
    }
} 

“某人”通过“朋友”和“陌生人”联系
public class SomeOne {
    public void play(Friend friend){
        System.out.println("someone play");
        friend.play();
        Stranger stranger = friend.getStranger();
        stranger.play();
    }
}
public class Friend {
    public void play(){
        System.out.println("friends paly");
    }
    public Stranger getStranger() {
        return new Stranger();
    }
}
public class Stranger {
    public void play(){
        System.out.println("stranger play");
    }
}
public class MainClass {
    public static void main(String[] args) {
        SomeOne zhangsan = new SomeOne();
        zhangsan.play(new Friend());
    }
}
在SomeOne类中还有Stranger,而且“某人”和“朋友”的关系设置也不太合理,继续修改为:
public class SomeOne {
    private Friend friend;
    public Friend getFriend() {
        return friend;
    }
    public void setFriend(Friend friend) {
        this.friend = friend;
    }
    public void play(Friend friend){
        System.out.println("someone play");
        friend.play();
    }
}
public class Friend {
    public void play(){
        System.out.println("friends paly");
    }
    public void playWithStranger() {
        Stranger stranger = new Stranger();
        stranger.play();
    }
}
public class Stranger {
    public void play(){
        System.out.println("stranger play");
    }
}
public class MainClass {
    public static void main(String[] args) {
        SomeOne zhangsan = new SomeOne();
        zhangsan.setFriend(new Friend());
        zhangsan.getFriend().playWithStranger();
    }
}
在外观模式和终结者模式中,迪米特法则很明显。
6、接口隔离法则(Interface Segregation Principle)
客户端不应该依赖那些它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
如下图所示,类A依赖接口I中的方法1、方法2、方法3,类B是对类A依赖的实现。类C依赖接口I中的方法1、方法4、方法5,类D是对类C依赖的实现。对于类B和类D来说,虽然他们都存在着用不到的方法(也就是图中红色字体标记的方法),但由于实现了接口I,所以也必须要实现这些用不到的方法。

interface I {
    public void method1();
    public void method2();
    public void method3();
    public void method4();
    public void method5();
}
class A{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method2();
    }
    public void depend3(I i){
        i.method3();
    }
}
class B implements I{
    public void method1() {
        System.out.println("类B实现接口I的方法1");
    }
    public void method2() {
        System.out.println("类B实现接口I的方法2");
    }
    public void method3() {
        System.out.println("类B实现接口I的方法3");
    }
    //对于类B来说,method4和method5不是必需的,但是由于接口A中有这两个方法,
    //所以在实现过程中即使这两个方法的方法体为空,也要将这两个没有作用的方法进行实现。
    public void method4() {}
    public void method5() {}
}
class C{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method4();
    }
    public void depend3(I i){
        i.method5();
    }
}
class D implements I{
    public void method1() {
        System.out.println("类D实现接口I的方法1");
    }
    //对于类D来说,method2和method3不是必需的,但是由于接口A中有这两个方法,
    //所以在实现过程中即使这两个方法的方法体为空,也要将这两个没有作用的方法进行实现。
    public void method2() {}
    public void method3() {}
    public void method4() {
        System.out.println("类D实现接口I的方法4");
    }
    public void method5() {
        System.out.println("类D实现接口I的方法5");
    }
}
public class Client{
    public static void main(String[] args){
        A a = new A();
        a.depend1(new B());
        a.depend2(new B());
        a.depend3(new B());
        C c = new C();
        c.depend1(new D());
        c.depend2(new D());
        c.depend3(new D());
    }
} 
我们需要将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系,也就是采用接口隔离原则,如下所示。

interface I1 {
    public void method1();
}
interface I2 {
    public void method2();
    public void method3();
}
interface I3 {
    public void method4();
    public void method5();
}
class A{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I2 i){
        i.method2();
    }
    public void depend3(I2 i){
        i.method3();
    }
}
class B implements I1, I2{
    public void method1() {
        System.out.println("类B实现接口I1的方法1");
    }
    public void method2() {
        System.out.println("类B实现接口I2的方法2");
    }
    public void method3() {
        System.out.println("类B实现接口I2的方法3");
    }
}
class C{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I3 i){
        i.method4();
    }
    public void depend3(I3 i){
        i.method5();
    }
}
class D implements I1, I3{
    public void method1() {
        System.out.println("类D实现接口I1的方法1");
    }
    public void method4() {
        System.out.println("类D实现接口I3的方法4");
    }
    public void method5() {
        System.out.println("类D实现接口I3的方法5");
    }
} 
接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
采用接口隔离原则对接口进行约束时,要注意以下几点:
- 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
 - 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
 - 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
 
7 .合成/聚合复用原则(Composite/Aggregate Reuse Principle)
这个原则涉及到为聚合(Aggregation) 关系和合成(Composition) 关系,在“设计模式--UML类图”中有所介绍。
这个原则指复用时要尽量使用合成/聚合关系(关联关系),少用继承。
通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,所以这种复用又称“白箱”复用,如果基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;而且继承只能在有限的环境中使用(如类没有声明为不能被继承)。
由于组合或聚合关系可以将已有的对象(也可称为成员对象)纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以使得成员对象的内部实现细节对于新对象不可见,所以这种复用又称为“黑箱”复用,相对继承关系而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要有选择性地调用成员对象的操作;合成复用可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。
总之,合成/聚合可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。使用继承要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,但滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。
如果两个类之间是“Has-A”的关系应使用合成或聚合,如果是“Is-A”关系可使用继承。"Is-A"是严格的分类学意义上的定义,意思是一个类是另一个类的"一种";而"Has-A"则不同,它表示某一个角色具有某一项责任。
例子:我们需要办理一张银行卡,如果银行卡默认都拥有了存款、取款和透支的功能,那么我们办理的卡都将具有这个功能,此时使用了继承关系:

为了灵活地拥有各种功能,此时可以分别设立储蓄卡和信用卡两种,并有银行卡来对它们进行聚合使用。此时采用了合成复用原则:

java设计模式--七大原则的更多相关文章
- 图解Java设计模式之设计模式七大原则
		
图解Java设计模式之设计模式七大原则 2.1 设计模式的目的 2.2 设计模式七大原则 2.3 单一职责原则 2.3.1 基本介绍 2.3.2 应用实例 2.4 接口隔离原则(Interface S ...
 - Java设计模式六大原则-2
		
Java设计模式六大原则-2 做Java程序开发的每天都在使用JDK,Spring,SpringMvc,Mybatis,Netty,MINA等框架,但很少有人懂得背后的原理.即使打开跟下原码也是一头雾 ...
 - Java设计模式六大原则-1
		
Java设计模式六大原则-1 做Java程序开发的每天都在使用JDK,Spring,SpringMvc,Mybatis,Netty,MINA等框架,但很少有人懂得背后的原理.即使打开跟下原码也是一头雾 ...
 - Java 设计模式六原则及23中常用设计模式
		
一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...
 - 设计模式七大原则(C++描述)
		
前言 最近在学习一些基本的设计模式,发现很多博客都是写了六个原则,但我认为有7个原则,并且我认为在编码中思想还是挺重要,所以写下一篇博客来总结下 之后有机会会写下一些设计模式的博客(咕咕咕...... ...
 - Java设计模式 --- 七大常用设计模式示例归纳
		
设计模式分为三种类型,共23种: 创建型模式:单例模式.抽象工厂模式.建造者模式.工厂模式.原型模式 结构型模式:适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式 行为型模式:模 ...
 - Java设计模式六大原则
		
一.单一职责原则 单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小.单一职责原则定义如下: 单一职责原则(Single Responsibility Principle, SRP):一个 ...
 - Java设计模式六大原则之场景应用分析
		
定义:不要存在多于一个导致类变更的原因. 通俗的说.即一个类仅仅负责一项职责. 问题由来:类T负责两个不同的职责:职责P1,职责P2.当由于职责P1需求发生改变而须要改动类T时,有可能会导致原本执行正 ...
 - JAVA设计模式-设计原则
		
6大原则: 单一职责原则 里氏替换原则 依赖倒置原则 接口隔离原则 迪米特法则 开闭原则 一.单一职责原则 定义:应该有且仅有一个原因引起类的变更 带来的好处: 类的复杂性降低,实现什么职责有清晰明确 ...
 
随机推荐
- python使用set来去重碰到TypeError: unhashable type
			
新版:Python 的 unhashable type 错误分析及解决 python使用set来去重是一种常用的方法. 一般使用方法如下: # int a = [1, 2, 3, 4, 5, 1, 2 ...
 - Redis 资料整理
			
Redis is an open source, BSD licensed, advanced key-value store. Redis is often referred to as a dat ...
 - 20145221 《Java程序设计》第九周学习总结
			
20145221 <Java程序设计>第九周学习总结 教材学习内容总结 整合数据库 JDBC入门 JDBC是用于执行SQL的解决方案,开发人员使用JDBC的标准接口,数据库厂商则对接口进行 ...
 - 高级bash脚本编程(三)
			
高级bash脚本编程 知识点 compound 和 comparison -a 逻辑与 exp1 -a exp2 如果表达式 exp1 和 exp2 都为真的话,那么结果为真. -o 逻辑或 exp1 ...
 - 20145315 《Java程序设计》第五周学习总结
			
20145315 <Java程序设计>第五周学习总结 教材学习内容总结 第八章 8.1语法与继承架构 8.1.1使用try,catch 所有的错误都会被打包为对象,使用try,catch可 ...
 - LCD1602小程序
			
1显示数据 typedef struct { unsigned long int mL_data; unsigned long int L_data; unsigned long int M3_dat ...
 - POJ 3268 Silver Cow Party(最短路&Dijkstra)题解
			
题意:有n个地点,有m条路,问从所有点走到指定点x再走回去的最短路中的最长路径 思路:用Floyd超时的,这里用的Dijkstra. Dijkstra感觉和Prim和Kruskal的思路很像啊.我们把 ...
 - 转:常用svn命令
			
在公司需要提交代码,常用的就是co.ci.add.up.和log 首先 svn help 可以看到 svn 所支持的全部命令: 命令不多,如果用过Tortoise SVN的客户端,从字面上也不难理解这 ...
 - UOJ #122 【NOI2013】 树的计数
			
题目链接:树的计数 这道题好神啊……正好有人讲了这道题,那么我就写掉吧…… 首先,为了方便考虑,我们可以把节点重标号,使得\(bfs\)序变成\(1,2,3,\dots,n\),那么显然树的深度就是\ ...
 - kissy初体验-waterfall
			
目录: 1. 功能介绍 2. waterfall样例展示 3. 使用说明 4. 遇到过的问题 5. 总结 1. 功能介绍 现在越来越多的网站开始瀑布流方式布局,瀑布流式布局(百度百科:瀑布流),是比较 ...