单一职责原则
定义很简单:一个类只负责一项职责,这就叫单一职责原则。先来看一个Demo
public class SingleResponsibility1 {
public static void main(String[] args) {
new Animal().eat("猫");
new Animal().eat("兔子");
//运行结果:
/**
*猫吃肉
*兔子吃肉
*/
}
}
class Animal{
public void eat(String animal){
System.out.println(animal+"吃肉");
}
}
兔子并不是吃肉的,很明显,这里的Animal类既负责吃肉的动物,又负责吃素的动物,违背了单一职责原则。而最容易想到的改进方法就是将Animal类细分为肉食动物类和素食动物类两个类:
public class SingleResponsibility2 {
public static void main(String[] args) {
new MeatAnimal().eat("猫");
new GrassAnimal().eat("兔子");
//运行结果:
/**
*猫吃肉
*兔子吃草
*/
}
}
class MeatAnimal{
public void eat(String animal){
System.out.println(animal+"吃肉");
}
}
class GrassAnimal{
public void eat(String animal){
System.out.println(animal+"吃草");
}
}
但是这种方式不仅要将原来的类分解,还要改动客户端(main方法)里的代码,因此代码的可维护性较差,不是理想的方案,下面再给出一种改动方案:
public class SingleResponsibility3 {
public static void main(String[] args) {
new Animal().eat1("猫");
new Animal().eat2("兔子");
}
}
class Animal{
public void eat1(String animal){
System.out.println(animal+"吃肉");
}
public void eat2(String animal){
System.out.println(animal+"吃草");
}
}
这种方案实质上并不是类的单一职责原则,而是方法上的单一职责原则,相比上面两种要好一些。这三种方案,一种比一种好,但第三种也不是最好的,总之,如果仅仅只用单一职责原则,确实能够对代码优化,但要达到最优,有时候用的不仅仅是一种原则。七大原则中的好几条可能要一起用才行,还是这个例子,看第四个Demo
public class SingleResponsibility4 {
public static void main(String[] args) {
new Animal().eat(new Cat());
new Animal().eat(new Rabbit());
}
}
class Animal{
//Animal的eat方法是对接口Eat的依赖
public void eat(Eat eat){
System.out.println(eat.getEatType());
}
}
interface Eat{
public String getEatType();
}
class Cat implements Eat{
@Override
public String getEatType() {
return "猫吃肉";
}
}
class Rabbit implements Eat{
@Override
public String getEatType() {
return "兔子吃草";
}
}
别看这个代码这么长,但是第四种相比前三种来说,可维护性可扩展性要高得多,这种方式,既遵守了单一职责原则,又遵守了依赖倒转原则,就是我上面说的,同时遵守多种原则的情况,简单说说这第四种为什么可维护性和可扩展性高?
- 如果现在新增了一个长颈鹿的动物,长颈鹿是吃树叶的
- 那么main方法里之前的东西不用改,只需要往后面加就行了(可扩展性体现)
- Animal类完全不需要任何修改
- Eat接口完全不需要任何修改
- 只需要新增一个Giraffe类去实现Eat接口,并重写getEatType方法就ok
- 从上面我们可以看出,一旦实际需求发送变化,只需要做很小的改动,并且只是“增加”/“删除”这种操作,完全没有“修改”这种操作
再来看看增加一个长颈鹿,代码怎么写:
public class SingleResponsibility4 {
public static void main(String[] args) {
new Animal().eat(new Cat());
new Animal().eat(new Rabbit());
new Animal().eat(new Giraffe());
}
}
class Animal{
//Animal的eat方法是对接口Eat的依赖
public void eat(Eat eat){
System.out.println(eat.getEatType());
}
}
interface Eat{
public String getEatType();
}
class Cat implements Eat{
@Override
public String getEatType() {
return "猫吃肉";
}
}
class Rabbit implements Eat{
@Override
public String getEatType() {
return "兔子吃草";
}
}
class Giraffe implements Eat{
@Override
public String getEatType() {
return "长颈鹿吃树叶";
}
}
接口隔离原则
定义:一个类对另一个类的依赖应该建立在最小的接口上;先看一个UML图:
做个说明:
- A通过Intergace1会依赖B,但A中只会使用到接口的1,2,3这三个方法
- C通过Intergace1会依赖D,但C中只会使用到接口的1,4,5这三个方法
先按照以上的UML图来写下代码:
public class InterfacePrinciple {
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.depend4(new D());
c.depend5(new D());
/**
* 运行结果:
* B实现了operation1方法
* B实现了operation2方法
* B实现了operation3方法
* D实现了operation1方法
* D实现了operation4方法
* D实现了operation5方法
*/
}
}
interface Interface1{
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
class B implements Interface1{
@Override
public void operation1() {
System.out.println("B实现了operation1方法");
}
@Override
public void operation2() {
System.out.println("B实现了operation2方法");
}
@Override
public void operation3() {
System.out.println("B实现了operation3方法");
}
@Override
public void operation4() {
System.out.println("B实现了operation4方法");
}
@Override
public void operation5() {
System.out.println("B实现了operation5方法");
}
}
class D implements Interface1{
@Override
public void operation1() {
System.out.println("D实现了operation1方法");
}
@Override
public void operation2() {
System.out.println("D实现了operation2方法");
}
@Override
public void operation3() {
System.out.println("D实现了operation3方法");
}
@Override
public void operation4() {
System.out.println("D实现了operation4方法");
}
@Override
public void operation5() {
System.out.println("D实现了operation5方法");
}
}
class A{
public void depend1(Interface1 m){
m.operation1();
}
public void depend2(Interface1 m){
m.operation2();
}
public void depend3(Interface1 m){
m.operation3();
}
}
class C{
public void depend1(Interface1 m){
m.operation1();
}
public void depend4(Interface1 m){
m.operation4();
}
public void depend5(Interface1 m){
m.operation5();
}
}
从这个代码以及运行结果来看,B和D两个类都实现了Interface1里的所有方法,然而A和C通过接口去依赖BD,仅仅只用到了一部分方法,也就是说,对B来说,operation4和operation5白写了,因为A根本用不到这两个。同样的,对D来说,operation2和operation3也白写了,C也用不到这两个。因此,以上的UML类图需要做出改动:
这一种设计方案,就叫做接口隔离,具体代码我就懒得贴上来了,总之,接口隔离的目的,就是:用不到的方法就隔离,手法是拆分原来的接口,分别去实现和依赖。
依赖倒转原则
四句话可以概括它:
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转的本质是面向接口编程
- 抽象比细节要稳定的多,抽象指的是接口或抽象类,细节指的是实现类/继承类
在上面单一职责原则的最后一个Demo的代码就用到了依赖倒转原则,现在再单独写一个别的:
public class DependenceInversion {
public static void main(String[] args) {
Phone phone = new Phone();
phone.receive(new Email());
/**
* 运行结果:
* 收到电子邮件信息,信息内容为:“hello,jack”
*/
}
}
class Email {
public String getInfo() {
return "收到电子邮件信息,信息内容为:“hello,jack”";
}
}
class Phone {
public void receive(Email email) {
System.out.println(email.getInfo());
}
}
来分析一下以上代码:
- 手机收到信息,如果收到的是Email信息,就像上面那样写ok,如果收到的是微信信息,QQ信息等,那么在Phone这个类里面,就还要增加WeiXin以及QQ的receive方法
- 还能怎么做呢?还可以引入一个接口InfoSource,表示信息来源;而Email,QQ,WeiXin就属于一系列的信息来源,因此,它们可以作为InfoSource的实现类,下面用代码来写一个:
public class DependenceInversion {
public static void main(String[] args) {
Phone phone = new Phone();
phone.receive(new Email());
phone.receive(new WeiXin());
/**
* 运行结果:
* 收到电子邮件信息,信息内容为:“hello,jack”
* 收到微信信息,信息内容为:“GoodBye,jack”
*/
}
}
class Phone {//这里是对接口的依赖,因此即便有新的信息种类来源,这里也不需要更改
public void receive(InfoSource infoSource) {
System.out.println(infoSource.getInfo());
}
}
interface InfoSource {
String getInfo();
}
class Email implements InfoSource {
@Override
public String getInfo() {
return "收到电子邮件信息,信息内容为:“hello,jack”";
}
}
class WeiXin implements InfoSource {
@Override
public String getInfo() {
return "收到微信信息,信息内容为:“GoodBye,jack”";
}
}
依赖关系的三种传递方式
- 基于接口的传递
public class DependenceInversion {
public static void main(String[] args) {
new Email().receive(new InfoSource() {
@Override
public void getInfo() {
System.out.println("收到Email信息,内容为:“你好帅呀”");
}
});
new Email().send(new InfoSource() {
@Override
public void getInfo() {
System.out.println("发送Email信息,内容为:“你更帅”");
}
});
new WeiXin().receive(new InfoSource() {
@Override
public void getInfo() {
System.out.println("收到微信信息,内容为:“你好丑啊”");
}
});
new WeiXin().send(new InfoSource() {
@Override
public void getInfo() {
System.out.println("发送微信信息,内容为:“你比我更丑”");
}
});
/**
* 运行结果:
* 收到Email信息,内容为:“你好帅呀”
* 发送Email信息,内容为:“你更帅”
* 收到微信信息,内容为:“你好丑啊”
* 发送微信信息,内容为:“你比我更丑”
*/
}
}
interface ReceiveOrSend {//接收或者发送
//两个抽象方法都接收了接口
void receive(InfoSource infoSource);
void send(InfoSource infoSource);
}
interface InfoSource {//信息
void getInfo();//信息具体内容
}
class Email implements ReceiveOrSend {
@Override
public void receive(InfoSource infoSource) {
infoSource.getInfo();
}
@Override
public void send(InfoSource infoSource) {
infoSource.getInfo();
}
}
class WeiXin implements ReceiveOrSend {
@Override
public void receive(InfoSource infoSource) {
infoSource.getInfo();
}
@Override
public void send(InfoSource infoSource) {
infoSource.getInfo();
}
}
- 基于构造器传递
public class DependenceInversion {
public static void main(String[] args) {
new Email(new InfoSource() {
@Override
public void getInfo() {
System.out.println("收到Email信息,内容为:“你好帅呀”");
}
}).receive();
new Email(new InfoSource() {
@Override
public void getInfo() {
System.out.println("发送Email信息,内容为:“你更帅”");
}
}).send();
new WeiXin(new InfoSource() {
@Override
public void getInfo() {
System.out.println("收到微信信息,内容为:“你好丑啊”");
}
}).receive();
new WeiXin(new InfoSource() {
@Override
public void getInfo() {
System.out.println("发送微信信息,内容为:“你比我更丑”");
}
}).send();
/**
* 运行结果:
* 收到Email信息,内容为:“你好帅呀”
* 发送Email信息,内容为:“你更帅”
* 收到微信信息,内容为:“你好丑啊”
* 发送微信信息,内容为:“你比我更丑”
*/
}
}
interface ReceiveOrSend {//接收或者发送
//两个抽象方法没有接收接口
void receive();
void send();
}
interface InfoSource {//信息
void getInfo();//信息具体内容
}
class Email implements ReceiveOrSend {
public InfoSource mInfoSource;
public Email(InfoSource infoSource){
mInfoSource = infoSource;
}
@Override
public void receive() {
mInfoSource.getInfo();
}
@Override
public void send() {
mInfoSource.getInfo();
}
}
class WeiXin implements ReceiveOrSend {
public InfoSource mInfoSource;
public WeiXin(InfoSource infoSource){
mInfoSource = infoSource;
}
@Override
public void receive() {
mInfoSource.getInfo();
}
@Override
public void send() {
mInfoSource.getInfo();
}
}
- 通过setter方法传递
public class DependenceInversion {
public static void main(String[] args) {
Email email = new Email();
email.setInfo(new InfoSource() {
@Override
public void getInfo() {
System.out.println("收到Email信息,内容为:“你好帅呀”");
}
});
email.receive();
email.setInfo(new InfoSource() {
@Override
public void getInfo() {
System.out.println("发送Email信息,内容为:“你更帅”");
}
});
email.send();
WeiXin weiXin = new WeiXin();
weiXin.setInfo(new InfoSource() {
@Override
public void getInfo() {
System.out.println("收到微信信息,内容为:“你好丑啊”");
}
});
weiXin.receive();
weiXin.setInfo(new InfoSource() {
@Override
public void getInfo() {
System.out.println("发送微信信息,内容为:“你比我更丑”");
}
});
weiXin.send();
/**
* 运行结果:
* 收到Email信息,内容为:“你好帅呀”
* 发送Email信息,内容为:“你更帅”
* 收到微信信息,内容为:“你好丑啊”
* 发送微信信息,内容为:“你比我更丑”
*/
}
}
interface ReceiveOrSend {//接收或者发送
//两个抽象方法没有接收接口
void receive();
void send();
//新增了一个setter方法
void setInfo(InfoSource infoSource);
}
interface InfoSource {//信息
void getInfo();//信息具体内容
}
class Email implements ReceiveOrSend {
public InfoSource mInfoSource;
@Override
public void receive() {
mInfoSource.getInfo();
}
@Override
public void send() {
mInfoSource.getInfo();
}
@Override
public void setInfo(InfoSource infoSource) {
mInfoSource = infoSource;
}
}
class WeiXin implements ReceiveOrSend {
public InfoSource mInfoSource;
@Override
public void receive() {
mInfoSource.getInfo();
}
@Override
public void send() {
mInfoSource.getInfo();
}
@Override
public void setInfo(InfoSource infoSource) {
mInfoSource = infoSource;
}
}
七大原则已经写了3个,后面4个下一篇博客再接着写