设计原则-

单一职责:每个类或每个方法每个框架只做一件事
反例:统计文本文件中有多少个单词

public static void main(String[] args) {
try {
//=========================负责加载路径下的文件
Reader in = new FileReader(“F:\1.txt”);
BufferedReader br = new BufferedReader(in);
String line = null;
StringBuilder sb = new StringBuilder("");
while ((line =br.readLine())!=null){
sb.append(line);
sb.append("");
}
//==========================负责根据正则分割字符串
String [] words = sb.toString().split("[^a-zA-Z]+");
System.out.println(words.length);
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}

上面的写法就违反了单一职责,同一个main方法中,我们即让它去加载文件,也让它去做分割,这样做的坏处是,每当我们需要调用其中的一个功能时(如我有文件只需要分割),仍需要重写一遍。

正例:

//==============只负责根据路径加载文件
public String loadFile(String path) throws IOException {
Reader in = new FileReader(path);
BufferedReader br = new BufferedReader(in);
String line = null;
StringBuilder sb = new StringBuilder("");
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append("");
}
br.close();
return sb.toString();
}
//=========只负责字符串根据正则分割
public int Textlength(String sb, String regex){
String [] words = sb.split(regex);
return words.length;
}
public static void main(String[] args) throws IOException {
String str = loadFile(“F:\1.txt”);
String regex ="[^a-zA-Z]+";
System.out.println(Textlength(str,regex));
}

通过单一职责,可以提高代码的重用性,通过单一职责的方法得到的数据我们不再有耦合,拿来可以做的事也不再局限。

开闭原则:对扩展开放(新功能),对修改关闭(旧功能)
创建了一个汽车对象

当变化来临时,例如汽车的价格现在需要打折(打8折),这时,我们在Car的源代码中修改,就违反了开闭原则

反例:修改了源代码

public void setPrice(float price) {
this.price = (price*0.08f);
}
开发时,我们应该要去考虑变化的需求,属性会在任何时刻都有可能产生变化

对待变化时,我们应该选择扩展,而不是去修改源代码(ps:在强制要求符合开闭原则的情况下!)

正例:

class Car {
private String band;
private String color;
private float price;

    public String getBand() {
        return band;
    }

    public void setBand(String band) {
        this.band = band;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "band='" + band + '\'' +
                ", color='" + color + '\'' +
                ", price=" + price +
                '}';
    }
}

public static void main(String[] args) {
    Car car = new Car();
    car.setBand("bm");
    car.setColor("red");
    car.setPrice(13.3f);
    System.out.println(car.toString());
}

//进行扩展
class DiscountCar extends Car{
@Override
public void setPrice(float price) {
super.setPrice(price*0.08f);
}
}
public static void main(String[] args) {
//===使用向上转型时,方法的调用只和new的对象有关
Car car = new DiscountCar();
car.setBand(“bm”);
car.setColor(“red”);
car.setPrice(130000f);
System.out.println(car.toString());
}

开闭原则应该遵循应用场景去考虑,如果源代码就是你自己写的,而且需求是稳定的,那么,直接修改源代码也是一个简单的做法,但当源代码是别人的代码或架构是,我们就要去符合开闭原则,防止破坏结构的完整性!

依赖倒置原则:上层不能依赖于下层,它们都应该依赖于抽象
UML类图规则:
关联:一个类的对象,作为另一个类的字段

画法:实线加箭头

class Test{
}
class Test2{
private Test test;
//Test2关联Test
//Test被关联
}

阳光E购平台 > 设计原则 > image2021-7-20_11-26-8.png

依赖:一个类的方法中定义了另一个类作为局部字段

画法:虚线加箭头

class Test{
}
class Test2{
pubilic void test(){
Test test= new Test;
}
}

阳光E购平台 > 设计原则 > image2021-7-20_11-27-18.png

继承:画法 实现加空心箭头

实现:画法 虚线加空心箭头

依赖倒置原则:
反例:人喂养动物

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 p = new Person();
    Dog d = new Dog();
    p.feed(d);
}

在上面的代码中,人要喂狗,依赖于有一条狗,人作为上层依赖于下层,这样有什么坏处呢?

坏处是,当变化来临时,比如,人又养了一只猫,那么上层人这个类当中,就必须在添加喂猫的方法,每当下层变动时,上层也会跟着变动,而我们希望下层变动时,上层不会跟着改变

正例:他们都应该依赖于抽象

interface Animal{
void eat();
}
static class Person{
public void feed(Animal animal){
animal.eat();
}
}
static class Dog implements Animal{
public void eat() {
System.out.println(“狗啃骨头”);
}
}
public static void main(String[] args) {
Person p = new Person();
Dog d = new Dog();
p.feed(d);
}

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

接口隔离原则:设计接口时,接口的抽象应该是有意义的
反例:动物接口中定义的方法并不是被所有动物需要的

interface Animal{
void eat();
void fly();
void swim();
}
class Bird implements Animal{
@Override
public void eat() {
System.out.println(“吃”);
}
@Override
public void fly() {
System.out.println(“飞”);
}
//======鸟不会游泳,并不需要实现
@Override
public void swim() {
System.out.println(“游泳”);
}
}

正例:接口抽象出有意义的层级,供需要的类去实现

interface Flyable{
void fly();
}
interface Swimable{
void swim();
}
interface Eatable{
void eat();
}
class Bird implements Flyable,Eatable{

}
class Dog implements Swimable,Eatable{

}

客户端不应该依赖那些它不需要的接口。

一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可

迪米特法则(最少知道原则):封装,只和朋友通信
什么是朋友?
1.类中的字段

2.方法的返回值

3.方法的参数

4.方法中的实例对象

5.对象本身

6.集合中的泛型

最少知道原则
反例:关闭电脑的流程

class Computers{
public void closeFile(){
System.out.println(“关闭文件”);
}
public void closeScreen(){
System.out.println(“关闭屏幕”);
}
public void powerOff(){
System.out.println(“断电”);
}
}
class Person{
private Computers computers;
public void offComputers(){
computers.closeFile();
computers.closeScreen();
computers.powerOff();
}
}

当用户关闭电脑时,需要调用计算机的各个方法,但是这些方法的细节太多了,会出现用户流程出错,遗漏调用等等,对于用户来言,他只需要知道关机按钮就够了

正例:封装细节,提供接口

class Computers{
public void closeFile(){
System.out.println(“关闭文件”);
}
public void closeScreen(){
System.out.println(“关闭屏幕”);
}
public void powerOff(){
System.out.println(“断电”);
}
public void offComputers(){
closeFile();
closeScreen();
powerOff();
}
}
class Person{
private Computers computers;
public void offComputers(){
computers.offComputers();
}
}

里氏替换原则: 任何能用父类对象的地方,都能透明的使用子类替换
ps:子类替换父类时,不能比父类的访问修饰更严格,不能抛出父类不存在的异常

使用时,需要考虑:

1.是否有is-a的关系

2.有is-a关系后,要考虑子类替换父类后会不会出现逻辑变化

反例:

class Counter {
public int add(int i,int j) {
return i+j;
}
}
class soonCounter extends Counter{
@Override
public int add(int i, int j) {
return i-j;
}
}
public static void main(String[] args) {
Counter c = new Counter();
System.out.println(c.add(100,200));
//结果300
Counter c = new soonCounter();
System.out.println(c.add(100,200));
//结果-100
}

我们要知道,在发生向上转型的过程时,方法的调用只和new的对象有关系,所以造成了不同的结果

使用里氏替换原则时,要知道 子类可以扩展父类的功能,但不能改变父类原有的功能

组合优于继承:复用别人代码时,应该使用组合
反例:继承hashset,重写父类add方法,每次add元素时count+1;

class Counter extends HashSet {
private int count =0;
public boolean add(int i) {
count++;
return super.add(i);
}
public int getCount(){
return count;
}
}

public static void main(String[] args) {
    Counter c = new Counter();
    c.add(1);
    c.add(2);
    c.add(3);
    System.out.println(c.getCount());
}

这样写会出现在hashset方法中,不只是add可以添加元素的问题,还有其他方式可以添加,如果是其他方式添加的,count就不会累加,这样是有问题的
@Override
public boolean addAll(Collection c) {
count=count+c.size();
return super.addAll©;
}

假如我们继续重写addAll方法添加相应的判断时,又会出现新的问题,我们的count并没有正确的累计,因为在HashSet的源码addAll方法中,回调了add方法,并没有解决需求

那我们不重写addAll,反正它会回调add,完成计数,就没有问题了吗?

其实并不能解决问题,hashset的源码我们不能保证永远不会更改,假如在下一个版本中,hashset的作者更改了addAll方法,那么我们的功能也会不能正常实现了!

当继承的父类作者不是我们自己的时候,我们没有办法保证父类代码不会变更,假如我们继承了这个父类,那么我们最好是只去复用父类的代码,避免去重写或新建方法,防止源码结构变更带来的打击

也就是说,在我们需要重用代码,并且重用的代码作者并不是我们自己的时候,我们要采用组合的方式。

正例:组合优于继承

static class Counter {
HashSet hashSet = new HashSet();
private int count =0;
public boolean add(int i) {
count++;
return hashSet.add(i);
}
public boolean addAll(Collection c) {
count=count+c.size();
return hashSet.addAll©;
}
public int getCount(){
return count;
}
}

public static void main(String[] args) {
    Counter c = new Counter();
    c.add(1);
    c.add(2);
    c.add(3);
    System.out.println(c.getCount());
}

这样来写,我们类中的add和addAll方法跟HashSet中的add和addAll方法的不在有关系,也能解决这个问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值