简述:单个类,单个方法或者单个框架只完成某一特定功能。

需求:统计文本文件中有多少个单词。

反例:

Copy
public class nagtive {
public static void main(String[] args) {
try{
//读取文件的内容
Reader in = new FileReader(“E:\1.txt”);
BufferedReader bufferedReader = new BufferedReader(in);

        String line = null;StringBuilder sb = new StringBuilder("");while((line =bufferedReader.readLine()) != null){sb.append(line);sb.append(" ");}//对内容进行分割String[] words = sb.toString().split("[^a-zA-Z]+");System.out.println(words.length);bufferedReader.close();} catch (IOException e) {e.printStackTrace();}
}

}
上面代码违反单一职责原则,同一个方法我们让它去做文件读取,还让他去做内容分割;当有需求变更(需要更换加载文件,统计文本文件中有多少个句子)时,我们需要重写整个方法。

正例:

Copy
public class postive {

public static StringBuilder loadFile(String fileLocation) throws IOException {//读取文件的内容Reader in = new FileReader("E:\\1.txt");BufferedReader bufferedReader = new BufferedReader(in);String line = null;StringBuilder sb = new StringBuilder("");while ((line = bufferedReader.readLine()) != null) {sb.append(line);sb.append(" ");}bufferedReader.close();return sb;
}public static String[] getWords(String regex, StringBuilder sb){//对内容进行分割return  sb.toString().split(regex);
}public static void main(String[] args) throws IOException {//读取文件的内容StringBuilder sb = loadFile("E:\\1.txt");//对内容进行分割String[] words = getWords("[^a-zA-Z]+", sb);System.out.println(words.length);
}

}
遵守单一原则,可以给我们带来的好处是,提高了代码的可重用性,同时还让得到的数据不再有耦合,可以用来完成我们的个性化需求。

开闭原则#
简述:对扩展(新功能)开放,对修改(旧功能)关闭

实体类设计:

Copy
public class Pen {
private String prod_name;
private String prod_origin;
private float prod_price;

public String getProd_name() {return prod_name;
}public void setProd_name(String prod_name) {this.prod_name = prod_name;
}public String getProd_origin() {return prod_origin;
}public void setProd_origin(String prod_origin) {this.prod_origin = prod_origin;
}public float getProd_price() {return prod_price;
}public void setProd_price(float prod_price) {this.prod_price = prod_price;
}@Override
public String toString() {return "Pen{" +"prod_name='" + prod_name + '\'' +", prod_origin='" + prod_origin + '\'' +", prod_price=" + prod_price +'}';
}

}
Copy
public static void main(String[] args) {
//输入商品信息
Pen redPen = new Pen();
redPen.setProd_name(“英雄牌钢笔”);
redPen.setProd_origin(“厂里”);
redPen.setProd_price(15.5f);
//输出商品信息
System.out.println(redPen);
}
需求:商品搞活动,打折8折销售。

反例:在实体类的源代码,修改 setProd_price方法

Copy
public void setProd_price(float prod_price) {
this.prod_price = prod_price * 0.8f;
}
违反了开闭原则,在源代码中修改,对显示原价这一功能进行了修改。

在开发时,我们应该,必须去考虑可能会变化的需求,属性在任何时候都可能发生改变,对于需求的变化,在要求遵守开闭原则的前提下,我们应该在开发中去进行扩展,而不是修改源代码。

正例:

Copy
public class discountPen extends Pen{
//用重写方法设置价格
@Override
public void setProd_price(float prod_price) {
super.setProd_price(prod_price * 0.8f);
}
}
Copy
public class postive {
public static void main(String[] args) {
//输入商品信息,向上转型调用重写方法设置价格
Pen redPen = new discountPen();
redPen.setProd_name(“英雄牌钢笔”);
redPen.setProd_origin(“厂里”);
redPen.setProd_price(15.5f);
//输出商品信息
System.out.println(redPen);
}
}
开闭原则并不是必须要一味地死守,需要结合开发场景进行使用,如果需要修改的源代码是自己写的,修改之后去完成需求,当然是简单快速的;但是如果源代码是别人写的,或者是别人的架构,修改是存在巨大风险的,这时候应该去遵守开闭原则,防止破坏结构的完整性。

接口隔离原则#
简述:设计接口的时候,接口的抽象应是具有特定意义的。需要设计出的是一个内聚的、职责单一的接口。“使用多个专门接口总比使用单一的总接口好。”这一原则不提倡设计出具有“普遍”意义的接口。

反例:动物接口中并不是所有动物都需要的。

Copy
public interface Animal {
void eat();
void fiy(); //泥鳅:你来飞?
void swim(); // 大雕:你来游?
}
Copy
class Bird implements Animal {
@Override
public void eat() {
System.out.println(“用嘴巴吃”);
}

@Override
public void fiy() {System.out.println("用翅膀飞");
}@Override
public void swim() {
//我是大雕不会游泳
}

}
接口中的 swim()方法在实际开发中,并不适用于该类。

正例:接口抽象出同一层级的特定意义,提供给需要的类去实现。

Copy
public interface Fly {
void fly();
}

public interface Eat {
void eat();
}

public interface Swim {
void swim();
}
Copy
public class Bird_02 implements Fly,Eat{
@Override
public void eat() {
System.out.println(“用嘴巴吃”);
}

@Override
public void fly() {System.out.println("用翅膀飞");
}//我是大雕不会游泳

}
客户端依赖的接口中不应该存在他所不需要的方法。

如果某一接口太大导致这一情况发生,应该分割这一接口,使用接口的客户端只需要知道他需要使用的接口及该接口中的方法即可。

依赖倒置原则#
简述:上层不能依赖于下层,他们都应该依赖于抽象。

需求:人喂养动物

反例:

Copy
public class negtive {

static class Person {public void feed(Dog dog){dog.eat();}
}static class Dog {public void eat() {System.out.println("主人喂我了。汪汪汪...");}
}public static void main(String[] args) {Person person= new Person();Dog dog = new Dog();person.feed(dog);
}

}
image-20200912204644913

这时候,Person内部的feed方法依赖于Dog,是上层方法中又依赖于下层的类。(人竟然依赖于一条狗?这算骂人吗?)

当有需求变更,人的宠物不止有狗狗,还可以是猫等等,这时候需要修改上层类,这带来的是重用性的问题,同时还违反上面提到的开闭原则。

正例:

image-20200912204707141

Copy
public class postive {
static class Person {
public void feed(Animal animal){
animal.eat();
}
}

interface Animal{public void eat();
}static class Dog implements Animal{public void eat() {System.out.println("我是狗狗,主人喂我了。汪汪汪...");}
}static class Cat implements Animal{public void eat() {System.out.println("我是猫咪,主人也喂我了。(我为什么要说也?)喵喵喵...");}
}public static void main(String[] args) {Person person= new Person();Dog dog = new Dog();Cat cat = new Cat();person.feed(dog);person.feed(cat);
}

}
这时候,Person内部的feed方法不在依赖于依赖于Dog或者Cat,而是不管是Person,还是Dog或者Cat,他们都依赖与Animal这一抽象类,都依赖于抽象类。

这时候,不管是曾经的上层代码,还是曾经的下层代码,都不会因为需求而改变。

依赖倒转原则就是指:代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。通过面向接口编程,抽象不应该依赖于细节,细节应该依赖于抽象。

迪米特法则(最少知道原则)#
简述:一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。

反例:

Copy
public class negtive {
class Computer{
public void closeFile(){
System.out.println(“关闭文件”);
}
public void closeScreen(){
System.out.println(“关闭屏幕”);
}
public void powerOff(){
System.out.println(“断电”);
}
}

class Person{private Computer computer;public void offComputer(){computer.closeFile();computer.closeScreen();computer.powerOff();}
}

}
这时候,Person 知道了 Computer的很多细节,对于用户来说不够友好,而且,用户还可能会调用错误,先断电,再保存文件,显然不符合逻辑,会导致文件出现未保存的错误。

其实对于用户来说,知道进行关机就行了。

正例:封装细节

Copy
public class postive {
class Computer{
public void closeFile(){
System.out.println(“关闭文件”);
}
public void closeScreen(){
System.out.println(“关闭屏幕”);
}
public void powerOff(){
System.out.println(“断电”);
}

    public void turnOff(){  //封装细节this.closeFile();this.closeScreen();this.powerOff();}
}class Person{private Computer computer;public void offComputer(){computer.turnOff();}
}

}
前面说的,只和朋友通信,不和陌生人说话。先来明确一下什么才叫做朋友:

什么是朋友?#
类中的字段
方法的返回值
方法的参数
方法中的实例对象
对象本身
集合中的泛型
总的来说,只要在自身内定义的就是朋友,通过其他方法得到的都只是朋友的朋友;

但是,朋友的朋友不是我的朋友。

举个反例:

Copy
public class negtive {

 class Market{private  Computer computer;public Computer getComputer(){return this.computer;}
}static class Computer{public  void  closeFile(){System.out.println("关闭文件");}public  void  closeScreen(){System.out.println("关闭屏幕");}public  void  powerOff(){System.out.println("断电");}
}class Person{private Market market;Computer computer =market.getComputer(); // //此时的 computer 并不是 Person 的朋友,只是 Market 的朋友。
}

}
在实际开发中,要完全符合迪米特法则,也会有缺点:

在系统里造出大量的小方法,这些方法仅仅是传递间接的调用,与系统的业务逻辑无关。

遵循类之间的迪米特法则会是一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联。但是,这也会造成系统的不同模块之间的通信效率降低,也会使系统的不同模块之间不容易协调。

因此,前人总结出,一些方法论以供我们参考:

优先考虑将一个类设置成不变类。

尽量降低一个类的访问权限。

谨慎使用Serializable。

尽量降低成员的访问权限。

虽然规矩很多,但是理论需要深刻理解,实战需要经验积累。路还很长。

里氏替换原则#
简述:任何能使用父类对象的地方,都应该能透明地替换为子类对象。

需求:将长方形的宽改成比长大 1 。

反例:在父类Rectangular下,业务场景符合逻辑。现有子类Square,替换后如何。

Copy
public class negtive {
static class Rectangular {
private Integer width;
private Integer length;

    public Integer getWidth() {return width;}public void setWidth(Integer width) {this.width = width;}public Integer getLength() {return length;}public void setLength(Integer length) {this.length = length;}}static class Square extends Rectangular {private Integer sideWidth;@Overridepublic Integer getWidth() {return sideWidth;}@Overridepublic void setWidth(Integer width) {this.sideWidth = width;}@Overridepublic Integer getLength() {return sideWidth;}@Overridepublic void setLength(Integer length) {this.sideWidth = length;}
}static class Utils{public static void transform(Rectangular graph){while ( graph.getWidth() <= graph.getLength() ){graph.setWidth(graph.getWidth() + 1);System.out.println("长:"+graph.getLength()+" : " +"宽:"+graph.getWidth());}}
}public static void main(String[] args) {
// Rectangular graph = new Rectangular();Rectangular graph = new Square();graph.setWidth(20);graph.setLength(30);Utils.transform(graph);
}

}
替换后运行将是无限死循环。

要知道,在向上转型的时候,方法的调用只和new的对象有关,才会造成不同的结果。在使用场景下,需要考虑替换后业务逻辑是否受影响。

由此引出里氏替换原则的使用需要考虑的条件:

是否有is-a关系
子类可以扩展父类的功能,但是不能改变父类原有的功能。
这样的反例还有很多,如:鸵鸟非鸟,还有咱们老祖宗早就说过的的春秋战国时期–白马非马说,都是一个道理。

组合优于继承#
简述:复用别人的代码时,不宜使用继承,应该使用组合。

需求:制作一个组合,该集合能够记录下曾经添加过多少元素。(不只是统计某一时刻)

反例 #1:

Copy
public class negtive_1 {

static class MySet extends HashSet{private int count = 0;public int getCount() {return count;}@Overridepublic boolean add(Object o) {count++;return super.add(o);}
}public static void main(String[] args) {MySet mySet = new MySet();mySet.add("111111");mySet.add("22222222222222");mySet.add("2333");Set hashSet = new HashSet();hashSet.add("集合+11111");hashSet.add("集合+22222222");hashSet.add("集合+233333");mySet.addAll(hashSet);System.out.println(mySet.getCount());
}

}
看似解决了需求,add 方法可以成功将count进行自加, addAll方法通过方法内调用add,可以成功将count进行增加操作。

缺陷:JDK版本如果未来进行更新,addAll方法不再通过方法内调用add,那么当调用addAll进行集合添加元素时,count将不无从进行自加。需求也将无法满足。

HashMap 就在 1.6 1.7 1.8就分别更新了三次。

反例 #2:

Copy
public class negtive_2 {

static class MySet extends HashSet{private int count = 0;public int getCount() {return count;}@Overridepublic boolean add(Object o) {count++;return super.add(o);}@Overridepublic boolean addAll(Collection c) {boolean modified = false;for (Object e : c)if (add(e))modified = true;return modified;}
}public static void main(String[] args) {MySet mySet = new MySet();mySet.add("111111");mySet.add("22222222222222");mySet.add("2333");Set hashSet = new HashSet();hashSet.add("集合+11111");hashSet.add("集合+22222222");hashSet.add("集合+233333");mySet.addAll(hashSet);System.out.println(mySet.getCount());
}

}
亲自再重写addAll方法,确保addAll方法一定能调用到add方法,也就能够对 count进行增加操作。

但是,问题还是有的:

缺陷:

如果未来,HashSet新增了一个addSome方法进行元素的添加,那就白给了。
重写了addAll、add这两个方法,如果JDK中其他类的某些方法依赖于HashMap中的这两个方法,那么JDK中其他类依赖于HashMap中的这两个方法的某些方法就会有出错、崩溃等风险。
这时候,可以得出一些结论:

当我们不属于继承父类的开发团队时,是没办法保证父类代码不会被修改,或者修改时一定被通知到,这时候,就可能会出现需求满足有缺陷的情况。所以,但我们去复用父类的代码时,避免去重写或者新建方法,这样可以防止源代码结构发生改变带来的打击。

也就是说,我们在重用代码时,应该是组合优于继承。

正例:

Copy
public class postive {

class MySet{private HashSet hashSet;private int count = 0;public int getCount() {return count;}public boolean add(Object o) {count++;return hashSet.add(o);}public boolean addAll(Collection c) {count += c.size();return hashSet.addAll(c);}
}public static void main(String[] args) {negtive_2.MySet mySet = new negtive_2.MySet();mySet.add("111111");mySet.add("22222222222222");mySet.add("2333");Set hashSet = new HashSet();hashSet.add("集合+11111");hashSet.add("集合+22222222");hashSet.add("集合+233333");mySet.addAll(hashSet);System.out.println(mySet.getCount());
}

}
亚马逊测评 www.yisuping.com

设计模式 #1(7大设计原则)相关推荐

  1. Java设计模式总结——6大设计原则

    从今年的七月份开始学习设计模式到9月底,设计模式全部学完了,在学习期间,总共过了两篇:第一篇看完设计模式后,感觉只是脑子里面有印象但无法言语.于是决定在看一篇,到9月份第二篇设计模式总于看完了,这一篇 ...

  2. C++ 设计模式(8大设计原则、23种设计模式)李建忠

    简 述: 设计模式,久闻大名.此记录学习 "C++ 设计模式 李建忠" 的札记,核心共 8大设计原则.23中设计模式.后发现 GitHub 也有类似笔记 Ref1 .Ref2 相关 ...

  3. 软件工程-23种设计模式和7大设计原则

    一.前言 软件工程这一个大的学习章节中,其中有23种软件设计模式和七种设计原则需要掌握,下面为大家梳理出相关的知识点. 二.面向对象软件设计原则 1.单一职责原则(Single responsibil ...

  4. 设计模式概述—6大设计原则

    第一部分:6大设计原则 六大设计原则: 1)Single Responsibility Principle:单一职责原则 2)Open Closed Principle:开闭原则 3)Liskov S ...

  5. Java设计模式GOF之6大设计原则

    Java设计模式GOF之6大设计原则原则 1.开闭原则(Open Close Principle) 一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. 开闭原则是面向对象的可复用设计的第一块基石 ...

  6. Java的7大设计原则和23种设计模式

    目录 一.UML类图 二. 设计原则 1.单一职责原则 2.里氏替换原则 3.依赖倒置原则 4.接口隔离原则 5.迪米特法则-最少知道原则 6.开闭原则 7.组合/聚合复用原则 三.Java中的23 ...

  7. 学习6大设计原则、23种设计模式

    了解设计模式的朋友们,想必都听说过"六大设计原则"吧.其实最经典的 23 种设计模式中或多或少地都在使用这些设计原则,也就是说,设计模式是站在设计原则的基础之上的.所以在学习设计模 ...

  8. 设计模式01-七大设计原则

    设计模式01-七大设计原则 文章目录 设计模式01-七大设计原则 开闭原则-Open Close 依赖倒置原则-Dependence Inversion 单一职责原则-Simple ResponsiB ...

  9. 设计模式之禅《一》 大旗不挥,谁敢冲锋 ——6大设计原则

    设计模式之禅<一>大旗不挥,谁敢冲锋 --6大设计原则 <一> 六大原则 一:单一职责原则 1.单一职责原则最难划分的就是职责 2.有两个可以变化的原因放到了一个接口中,这就为 ...

  10. 设计模式 — 6大设计原则(依赖倒置和接口隔离原则)

    设计模式 依赖倒置原则 示例 一 示例 二 依赖的三种写法 总结 接口隔离原则 实例 一 总结 依赖倒置原则 依赖倒置原则(Dependence Inversion Principle,DIP)这个名 ...

最新文章

  1. 相比薪酬,学习效率提升才是创业公司最有价值的报酬
  2. 二维数组转稀疏数组,写入文件后再读取文件,将内容转回二维数组
  3. 深入Redis持久化
  4. Xamarin.ios引用第三方SDK
  5. java 多线程局域网快速传输文件,java大文件复制最高效方法多线程FileChannel
  6. 编程小白C语言例题4
  7. noVNC使用浏览器替代VNC客户端
  8. Unix网络编程第一卷学习总结
  9. 微信小程序 图片缓存
  10. 计算机切换用户界面,win7系统登录界面切换用户的方法
  11. stm32调试1.44寸TFT液晶屏过程遇到的问题
  12. 少儿编程到底学什么?
  13. 圣诞节要到了,如何制作自己的一个给头像戴帽子的小程序,请看这
  14. 自动化测试工具 Java等
  15. hdu 5761 Rower Bo 物理题
  16. C++编程技巧:内码的转换技术
  17. 理解Segment Routing和SDWAN
  18. libxml2如何配合php使用,libxml2使用简介-----(转载)
  19. 常用CDK生成Java算法(大数异或)
  20. 08.CSS3选择器、边框、背景、按钮

热门文章

  1. 【机器人关节空间与笛卡尔空间示教】
  2. 【UE4从零开始 027】插槽 Slot
  3. 电脑开机各种蓝屏错误代码,U盘重装系统彻底解决
  4. 人民币成功“入篮”SDR,这意味着什么?
  5. 15秒的倒计时和15分钟的倒计时
  6. 如何在C++中方便的将float、int等类型数据转换成string类型,并利用ROS中的std_msg/String发布出去
  7. 悬浮框支持可拖动(已解决拖动后刷新回到原点的问题)
  8. (三)java数据库篇笔记库(34)
  9. WIFI模块的STA模式和AP模式有什么区别?
  10. 缉拿隐藏进程以及隐藏CPU利用率的进程