龙空技术网

JAVA程序员必备知识——七大设计原则

紫薯炒蛋 59

前言:

今天兄弟们对“java设计原则详解”大约比较关怀,小伙伴们都需要分析一些“java设计原则详解”的相关知识。那么小编在网摘上收集了一些对于“java设计原则详解””的相关文章,希望小伙伴们能喜欢,咱们一起来学习一下吧!

设计模式不是一种新的语言,也不是什么新api,更不是什么新语法。设计模式是前辈们不断总结,不断打磨出的设计方法。不同的设计模式适用于不同的场景。设计模式公认的有23种,分别对应23种不同的设计场景。千万不要认为任何一种设计模式能解决任何问题,每一种设计模式只能用于适用的场景,不是万能的。设计模式不是万能的,有优点也有缺点,不能为了适用设计模式而使用设计模式。切记防止‘’‘模式的滥用’23种设计模式,背后其实是7大设计原则。也就是说,每一个设计模式都归属于一个或者多个设计原则。七大设计原则背后总结为一个字:分!

1.单一职责原则

百科定义:单一职责原则(SRP:Single responsibility principle)又称单一功能原则,它规定一个类应该只有一个发生变化的原因。通俗的讲就是每个方法,每个类,每个框架都只负责一件事情。

例如:Math.round()只负责完成四舍五入方法,其它不管(方法)

Read类只负责文件的读取(类)

SpringMVC只负责简化MVC的开发(框架)

反例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/11  */ public class NegtiveTest {     public static void main(String[] args) throws IOException {         //统计一个文本文件中有多少个单词         Reader reader = new FileReader("C:\\Users\\asus\\Desktop\\2.txt");         BufferedReader bufferedReader = new BufferedReader(reader);         String line = null;         StringBuilder stringBuilder = new StringBuilder();         while ((line = bufferedReader.readLine()) != null) {             stringBuilder.append(line);             stringBuilder.append(" ");         }         String[] strings = stringBuilder.toString().split("[^a-zA-Z]+");         System.out.println(strings.length);         bufferedReader.close();     } } //以上代码违反了单一职责原则: //1.代码重用性低 //2.可读性低

正例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/11  */ public class PositiveTest {     public static void main(String[] args) throws IOException {         //统计一个文本文件中有多少个单词         String string = loadFile("C:\\Users\\asus\\Desktop\\2.txt");         String[] strings = getWords(string);         System.out.println(strings.length);     }      /**      * 只负责返回单词数组      * @param string      * @return      */     private static String[] getWords(String string) {         String[] strings = string.toString().split("[^a-zA-Z]+");         return strings;     }      /**      * 只负责读取文件      *      * @param path      * @return      * @throws IOException      */     private static String loadFile(String path) throws IOException {         Reader reader = new FileReader(path);         BufferedReader bufferedReader = new BufferedReader(reader);         String line = null;         StringBuilder stringBuilder = new StringBuilder();         while ((line = bufferedReader.readLine()) != null) {             stringBuilder.append(line);             stringBuilder.append(" ");         }         bufferedReader.close();         return stringBuilder.toString();     } }
2.开闭原则(OCP)

百科定义:在面向对象编程领域中,开闭原则规定“软件中的对象(类**,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”。正确的理解是,对扩展新功能开放,对修改旧功能关闭

反例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/16  */ @Data public class Cellphone {     private String brand;     private Double price; ​     public void setPrice(Double price) {         this.price = price * 0.8;     } ​     public static void main(String[] args) {         Cellphone cellphone = new Cellphone();         cellphone.setBrand("华为");         cellphone.setPrice(1999.0);       //所有手机打8折,违反开闭原则的做法是打开Cellphone源码,在setPrice上修改         System.out.println(cellphone);     } }

正例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/16  */ @Data public class Cellphone {     private String brand;     private Double price; ​     public void setPrice(Double price) {         this.price = price;     } ​     public static void main(String[] args) {         Cellphone cellphone = new DiscountCellphone();         cellphone.setBrand("华为");         cellphone.setPrice(1999.0);         //所有手机打8折,符合开闭原则的做法是新建一个子类,继承Cellphone重写父类setPrice方法         System.out.println(cellphone);     } } ​ class DiscountCellphone extends Cellphone {     @Override     public void setPrice(Double price) {         super.setPrice(price * 0.8);     } }
3.接口隔离原则

1.客户端不应该依赖它不需要的接口

2.类间的依赖关系应该建立在最小接口上

反例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class InterfaceSegregationNegativeTest { ​ ​     public static void main(String[] args) {         Animal animal = new Dog(); //        animal.fly();         animal.run();     } } interface Animal {     void run(); ​     void swim(); ​     void fly(); } ​  class Dog implements Animal{ ​      @Override     public void run() {         System.out.println("Dog is running");     } ​     @Override     public void swim() {         System.out.println("Dog is swimming");     }     //狗不具备飞行的能力,不应该实现飞行的方法     @Override     public void fly() {         throw new RuntimeException("Dog can not fly");     } }

正例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class InterfaceSegregationPositiveTest {     public static void main(String[] args) {         Dog dog = new Dog();         dog.run();         Bird bird = new Bird();         bird.fly();     } } ​ interface RunAble {     void run(); } ​ interface SwimAble {     void swim(); } ​ interface Flyable {     void fly(); } ​ /**  * 狗会跑,会游泳,客户端只需要实现它需要的接口  */ class Dog implements RunAble, SwimAble { ​     @Override     public void run() {         System.out.println("Dog is running");     } ​     @Override     public void swim() {         System.out.println("Dog is swimming");     } } ​ /**  * 鸟会飞,只需要实现它需要的接口  */ class Bird implements Flyable { ​     @Override     public void fly() {         System.out.println("Bird is flying");     } }
4.依赖倒置原则

上层不应该依赖于下层,上层和下层都应该依赖于抽象的接口

反例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class DependencyInversionNegativeTest {     public static void main(String[] args) {         //Person类依赖于Dog和Cat类,如果要喂鸟等其它动物,就要改Person的源代码,不利于扩展         Person person = new Person();         Cat cat = new Cat();         cat.setName("小猫");         Dog dog = new Dog();         dog.setName("小狗");         person.feed2(dog);     } } ​ class Person {     void feed(Cat cat) {         System.out.println("人正在喂" + cat.getName());         cat.eat();     } ​     void feed2(Dog dog) {         System.out.println("人正在喂" + dog.getName());         dog.eat();     } } ​ @Data class Cat {     String name; ​     void eat() {         System.out.println("猫正在吃猫粮");     } } ​ @Data class Dog {     String name; ​     void eat() {         System.out.println("狗正在吃狗粮");     } }

正例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class DependencyInversionPositiveTest {     public static void main(String[] args) {         //Person类、Dog类、Cat类都依赖于抽象的Animal接口,如果需要喂鸟只需要新建一个Bird类实现Animal即可,利于扩展         Person person = new Person();         Cat cat = new Cat();         cat.setName("小猫");         Dog dog = new Dog();         dog.setName("小狗");         person.feed(cat);         person.feed(dog);     } } ​ class Person {     void feed(Animal animal) {         animal.eat();     } ​ } ​ interface Animal {     void eat(); } ​ @Data class Cat implements Animal {     String name; ​     public void eat() {         System.out.println("猫正在吃猫粮");     } } ​ @Data class Dog implements Animal {     String name; ​     public void eat() {         System.out.println("狗正在吃狗粮");     } }
5.迪米特法则(最少知道原则)

迪米特法则,也叫最少知道原则(封装)。一个类,对于其他类,要知道的越少越好,只和自己的朋友通信。哪些是自己的朋友?1、类中的字段2、方法的返回值 3、方法的参数 4、方法中实例化出的对象

反例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/18  */ public class demeterNegativeTest {     public static void main(String[] args) {         Person person = new Person();         person.shutdownComputer();     } } ​ class Computer {     public void saveData() {         System.out.println("保存数据");     } ​     public void killProcess() {         System.out.println("关闭程序");     } ​     public void closeScreen() {         System.out.println("关闭屏幕");     } ​     public void powerOff() {         System.out.println("关闭电源");     } } ​ class Person {     private Computer computer = new Computer();     //Person对于Computer的细节知道的太多。对于person只需要知道关机键在哪里就行,不需要知道细节     //这种做法提高了代码的复杂度     public void shutdownComputer() {         computer.saveData();         computer.killProcess();         computer.closeScreen();         computer.powerOff();     } }

正例:

 /**  * <p>  * <p>  *  * @author xmp  * @since 2020/9/18  */ public class DemeterPositiveTest {     public static void main(String[] args) {         Person person = new Person();         person.shutdownComputer();     } } class Computer {     public void saveData() {         System.out.println("保存数据");     } ​     public void killProcess() {         System.out.println("关闭程序");     } ​     public void closeScreen() {         System.out.println("关闭屏幕");     } ​     public void powerOff() {         System.out.println("关闭电源");     }     public void shutdown() {         saveData();         killProcess();         closeScreen();         powerOff();     } } ​ class Person {     private Computer computer = new Computer();     public void shutdownComputer() {         computer.shutdown();     } }
6.里氏替换原则

任何能使用父类对象的地方,都应该能透明地替换为子类对象。子类对象替换父类对象,语法不会报错,业务逻辑也不会出问题。

 /**  * 方法重写:在子类和父类中,出现返回类型相同、方法名相同、方法参数相同的方法时,构成方法重写  * 方法重写的两个限制:1.子类重写父类方法时,子类方法的修饰符不能比父类更严格 2.子类重写父类方法时,不能抛出比父类更多的异常  * 为什么要有这两个限制? 为了子类对象替换父类对象时,语法不报错,满足里氏替换原则。  */ class Fu {     public void f1(){              }     public void f2(){} } ​ class Zi extends Fu{        //编译报错'f1()' in 'Zi' clashes with 'f1()' in 'Fu'; attempting to assign weaker access privileges ('private'); was 'public'     private void f1() {              }         //编译报错'f2()' in 'Zi' clashes with 'f2()' in 'Fu'; overridden method does not throw 'java.lang.Exception'     public void f2() throws Exception{ ​     } }
7.组合优于继承

类与类之间的关系有3种:

1.继承:子类继承父类

2.依赖: 一个类的对象作为另一个类的局部变量

3.关联:一个类的对象作为另一个类的字段(属性)

"关联"可以细分为:1.组合(关系强,鸟和翅膀) 2.聚合(关系弱,大雁和雁群)

组合优于继承中的组合没有分的那么细,其实指的的就是关联关系。

例A:

 /**  * <p>  * 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class AppTest {     public static void main(String[] args) {         MySet set = new MySet();         Set set1 = new HashSet();         set1.add(4);         set1.add(5);         set.addAll(set1);         System.out.println(set.getCount());         //想要的结果是2结果返回了4         //原因是addAll方法回调了add方法。所以这种代码没有完成需求     } } ​ class MySet extends HashSet {     private int count = 0; ​     @Override     public boolean add(Object o) {         count++;         return super.add(o);     } ​     @Override     public boolean addAll(Collection c) {         count += c.size();         return super.addAll(c);     } ​     public int getCount() {         return count;     } }

例B:

 /**  * <p>  * 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)  * 针对例A中出现的问题,addAll方法会回调add方法,只需要去掉子类中的addAll方法,不去重写父类的addAll方法即可,  * 因为addAll会去回调add方法  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class AppTest {     public static void main(String[] args) {         MySet set = new MySet();         Set set1 = new HashSet();         set1.add(4);         set1.add(5);         set.addAll(set1);         System.out.println(set.getCount());         //结果返回的2,已经满足了需求。         //问题:目前满足需求的前提是addAll方法会去回调add方法,但是万一jdk在未来的版本里面不再回调add方法,那么我们自定义的MySet这段代码就会被"撼动"!         //例如:HashMap在JDK1.6、1.7、1.8中底层分别变换了3次!     } } ​ class MySet extends HashSet {     private int count = 0; ​     @Override     public boolean add(Object o) {         count++;         return super.add(o);     } ​ ​     public int getCount() {         return count;     } }

例C:

 /**  * <p>  * 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)  * 针对例B中出现的问题,MySet必需依赖一个事实:addAll方法必需回调add方法,但是JDK未来的版本无法做这个保证  * 修改代码如下:我们亲自重写addAll方法,保证一定会回调add方法  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class AppTest {     public static void main(String[] args) {         MySet set = new MySet();         Set set1 = new HashSet();         set1.add(4);         set1.add(5);         set.addAll(set1);         System.out.println(set.getCount());         //结果:2         //问题1:如果在新的JDK版本中,突然多了一个添加元素到集合的方法:addSome,这个方法是我们始料未及的,我们的MySet根本没有重写addSome方法。         //这样在新版本中,我们的MySet也继承了addSome方法,当使用addSome方法添加元素时,就不会去统计元素添加的记录。         //问题2:我们重写了addAll和add方法,但是在HashSet的所有方法中,可能会有一些方法依赖于addAll和add方法。我们没头没脑地重写了别人的方法,就会导致其他依赖于这些方法的方法出现问题!     } } ​ class MySet extends HashSet {     private int count = 0; ​     @Override     public boolean add(Object o) {         count++;         return super.add(o);     } ​     @Override     public boolean addAll(Collection c) {         boolean modified = false;         for (Object e : c)             if (add(e))                 modified = true;         return modified;     } ​     public int getCount() {         return count;     } }

例D:

 /**  * <p>  * 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)  * 针对例C中出现的问题  * 修改代码如下:1.我们不再重写addAll和add方法。2我们额外制作两个方法addAll2和add2方法,来替换addAll和add方法,而且还要在类的api文档里面说明方法怎么使用。  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class AppTest {     public static void main(String[] args) {         MySet set = new MySet();         Set set1 = new HashSet();         set1.add(4);         set1.add(5);         set.addAll2(set1);         System.out.println(set.getCount());         //结果:2         //此时,代码勉强满足了需求         //问题1:用户必须看了api文档才能知道怎么使用addAll2和add2方法,而且方法名不能写错         //问题2:万一在将来的某个版本JDK中,恰恰也出现了addAll2和add2方法,那就没辙了         //难道不能用继承了吗?     } } ​ class MySet extends HashSet {     private int count = 0; ​     public boolean add2(Object o) {         count++;         return super.add(o);     } ​     public boolean addAll2(Collection c) {         boolean modified = false;         for (Object e : c)             if (add2(e))                 modified = true;         return modified;     } ​     public int getCount() {         return count;     } }

例E:

 /**  * <p>  * 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)  * 针对例D中出现的问题  * 修改代码如下:1.我们的MySet不再继承HashSet 2.取而代之,我们让MySet和HashSet发生关联关系(组合)  * <p>  *  * @author xmp  * @since 2020/9/16  */ public class AppTest {     public static void main(String[] args) {         MySet set = new MySet();         Set set1 = new HashSet();         set1.add(4);         set1.add(5);         set.addAll(set1);         System.out.println(set.getCount());         //结果:2         //此时,可以说一声:完美         //问题1:难道以后不能用继承了吗?         //问题2:难道以后不能方法重写了吗?         //如果父类的作者和子类的作者不是同一个人,就不要继承,那么父类的作者不知道未来的子类会重写哪些方法,那么子类的作者也不知道未来的父类会修改或新增哪些方法         //如果父类和子类的作者是同一个人,那么可以大胆的使用继承,如果我们仅仅为了复用代码而且使用继承,难免会出现"沟通"上的问题。     } } ​ class MySet {     private HashSet hashSet = new HashSet();     private int count = 0; ​     public boolean add(Object o) {         count++;         return hashSet.add(o);     } ​     public boolean addAll(Collection c) {         count += c.size();         return hashSet.addAll(c);     } ​     public int getCount() {         return count;     } }

标签: #java设计原则详解