JAVA-6大设计原则

来源:互联网 发布:linux shell for循环 编辑:程序博客网 时间:2024/06/06 05:48

一 : 类单一职责原则:

   一个类只有一个引起这个类变化的原因。即一个类只完成一个功能,如果做不到一个类只完成一个功能,最少要保证一个方法只完成一个功能。

二:依赖倒置原则(DIP):

  而依赖倒置原则的本质骑士就是通过抽象(抽象类或接口)使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合。但是这个原则也是6个设计原则中最难以实现的了,如果没有实现这个原则,那么也就意味着开闭原则(对扩展开发,对修改关闭)也无法实现。

1、高层模块不应该依赖低层模块,两者都应该依赖于抽象(抽象类或接口)

2、抽象(抽象类或接口)不应该依赖于细节(具体实现类)

3、细节(具体实现类)应该依赖抽象

抽象:即抽象类或接口,两者是不能够实例化的。

细节:即具体的实现类,实现接口或者继承抽象类所产生的类,两者可以通过关键字new直接被实例化。

而依赖倒置原则的本质骑士就是通过抽象(抽象类或接口)使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合。但是这个原则也是6个设计原则中最难以实现的了,如果没有实现这个原则,那么也就意味着开闭原则(对扩展开发,对修改关闭)也无法实现。

依赖倒置有三种方式来实现

1、通过构造函数传递依赖对象

比如在构造函数中的需要传递的参数是抽象类或接口的方式实现。

2、通过setter方法传递依赖对象

即在我们设置的setXXX方法中的参数为抽象类或接口,来实现传递依赖对象

 

3、接口声明实现依赖对象

例如下面的例子

涂涂是个女僧

    public class Tutu { 

        //涂涂是个女孩,会煮面 

        public void cook(Noodles noodles) 

        { 

            noodles.eat(); 

        } 

    }

面条(目前只会煮面)

    public class Noodles { 

        //吃面条 

        public void eat() 

        { 

            System.out.println("涂涂吃面条..."); 

        } 

    }

涂涂坐在家里吃面(场景类)

    public class Home { 

        public static void main(Stringargs[]) 

        { 

            Tutu tutu = new Tutu(); 

            Noodles food = new Noodles(); 

            tutu.cook(food); 

        } 

}

运行结果:涂涂吃面条...

但是这有个问题,涂涂只会做面条,不可能每次都吃面条吧,天天吃面吃死你,所以在上面的Tutu类中的cook方法中,如果涂涂会做其他吃的,那岂不是更好。于是她向家庭主妇迈进了一步,使用了依赖倒置原则。

也就是涂涂通过学习还可以焖米饭,炒鱿鱼(虽然听着不爽,但是很好吃),京酱肉丝啊等等。要想在代码中实现,就需要实现两个接口:ITutu和IFood

    public interface ITutu{ 

        //这样就会做很多饭菜了 

        public void cook(IFoodfood); 

    }

实现类

    public class Tutuimplements ITutu { 

        @Override

        public void cook(IFoodfood) { 

            food.eat(); 

        } 

      

    }

食物接口

    public interface IFood{ 

        public voideat(); 

    }

这样就为扩展留出了很大的空间,方面扩展其他的类。也不会对细节有变动。以后涂涂想吃什么学一下就可以自己做了

实现面条

    public class Noodlesimplements IFood {

        @Override

        public void eat(){ 

           System.out.println("涂涂吃面条..."); 

        } 

    }

实现米饭

    public class Riceimplements IFood {  

        @Override

        public void eat(){ 

           System.out.println("涂涂吃米饭(终于吃上米饭了)..."); 

        } 

    }

场景类:涂涂在家里开吃了,想吃什么直接做就是了

    public class Home { 

        public static voidmain(String args[]) 

        { 

            //接口使不能实例化滴 

            ITutu tutu = newTutu(); 

            //实例化米饭,涂涂可以吃米饭了 

            IFood rice = newRice(); 

            //吃面条 

            //IFood noodles =new Noodles(); 

           tutu.cook(rice); 

        } 

    }  

这样各个类或模块的实现彼此独立,不互相影响,实现了。

三:里氏代换原则(LSP):

  所有引用基类的地方必须能够透明的使用其子类对象。也就是说,只要父类出现的地方子类就能够出现,而且替换为子类不会产生任何错误或异常。

一、子类必须完全实现父类的方法

    1、定义一个抽象类

    public abstractclass ViewPoint { 

        //去丽江旅游 

        publicabstract void where(); 

}

2、定义两个类实现这个抽象类

public class Lijiang extendsViewPoint { 

    @Override

    public void where() { 

        System.out.println("欢迎来到丽江..."); 

    } 

 

 

public class Zhangjiajie extendsViewPoint { 

    @Override

    public void where() { 

        System.out.println("欢迎来到张家界...");

    }

}

3、人物是涂涂,在里面设置类类型来传递参数。此时涂涂要去的旅游景点还是抽象的

    public class Tutu { 

        //定义要旅游的景点 

        private ViewPoint viewpoint; 

        //涂涂要去的景点 

        public void setViewPoint(ViewPointviewpoint) 

        { 

            this.viewpoint = viewpoint; 

        } 

        public void travelTo() 

        { 

            System.out.println("涂涂要去旅游了"); 

            viewpoint.where(); 

        } 

    }

 

 

4、场景类。设置具体要去的景点

    public class Sence { 

        public static void main(Stringargs[]) 

        { 

            Tutu tutu = new Tutu(); 

            //设置要去的旅游景点 

            tutu.setViewPoint(newLijiang()); 

            tutu.travelTo(); 

        } 

    }

5、运行结果:

涂涂要去旅游了

欢迎来到丽江...

    二、子类可以有自己的特性

也就是说在类的子类上,可以定义其他的方法或属性

三、覆盖或者实现父类的方法时输入参数可以被放大

父类能够存在的地方,子类就能存在,并且不会对运行结果有变动。反之则不行。

父类,say()里面的参数是HashMap类型,是Map类型的子类型。(因为子类的范围应该比父类大)

    

 

 

importjava.util.Collection; 

    import java.util.HashMap; 

    public class Father { 

        public Collection say(HashMap map) 

        { 

            System.out.println("父类被执行..."); 

            return map.values(); 

        } 

    }

子类,say()里面的参数变成了Map类型,Map范围比HashMap类型大,符合LSP原则。注意这里的say不是覆写父类的say,因为参数类型不同。而是重载。

    import java.util.Collection; 

    import java.util.Map; 

    /* 

     * 子类继承了父类的所有属性  

     */

    public class Son extends Father { 

        public Collection say(Map map)  //方法输入参数类型

        { 

            System.out.println("子类被执行..."); 

            return map.values(); 

        } 

  }

场景类

    import java.util.HashMap; 

    public class Home { 

        public static void main(Stringargs[]) 

        { 

            invoke(); 

        } 

        public static void invoke() 

        { 

            //父类存在的地方,子类就应该能够存在 

            //Father f = new Father(); 

           Son s = new Son();

            HashMap map = new HashMap(); 

            //f.say(map); 

            s.say(map); 

        } 

    }

无论是用父类还是子类调用say方法,得到的结果都是

父类被执行...

但是,如果将上面Father里的say参数改为Map,子类Son里的say参数改为HashMap,得到的结果就变成了

f.say(map)结果:父类被执行...

s.say(map)结果:子类被执行...

 

这样会造成逻辑混乱。所以子类中方法的前置条件必须与父类中被覆写的前置条件相同或者更宽。

四、覆写或者实现父类的方法时输出结果可以被缩小

其实与上面的类似,也就是父类能出现的地方子类就可以出现,而且替换为子类不会产生任何错误或者异常,使用者也无需知道是父类还是子类。但是反过来就不行了,有子类出现的地方,父类未必就适应。(毕竟子类的范围要>=父类的范围)

 

 

 

四:迪米特法则:

  一个类要尽量的封装自己,一个类只与自己的朋友类打交道一般朋友类是成员变量或者参数,非朋友类一般都是局部变量

五:接口隔离原则:

  一个接口完成的功能尽可能的单一,不要让一个接口承担过多的责任。

六:开闭原则:

  对扩展开放,对修改闭合

0 0