提示:文章编写以Java作为开发语言
文章目录
目录
文章目录
前言
一、设计模式是什么?
二、设计模式的七大原则
1.单一职责原则
2.接口隔离原则
3.依赖倒转原则(DIP)
4.里氏替换原则
5.开闭原则(OCP)
6.迪米特法则
7.合成复用原则
总结
前言
开发语言自发展到现在,出现了很多优秀的框架,而框架=注解+反射+设计模式,学习完Java,我们已经熟悉了反射的概念和使用方式,接下来我们简单深入了解一下设计模式。
一、设计模式是什么?
设计模式是我们在对开发需求进行设计时,为了追求代码精简易懂,程序各个模块实现高内聚低耦合等目标不断迭代衍生出来的一种编程规范。
二、设计模式的七大原则
1.单一职责原则
所谓单一职责原则宏观上来说是指一个类只做一件事。如果一个类负责了多个功能模块,则在修改其中某个模块而导致类发生改变时,可能导致其他的功能模块失效。
举一个我工作中违背单一职责而引发的悲剧:
这个对我来说可谓是印象深刻,因为之前我在开发的时候把好几个模块写在了一个接口(Controller->service->dao),由于这些模块都是我开发的,开始没出现什么问题,可是后来,因为我有了其他任务,于是我的接口被分出去一部分给同事加功能,然后出现了代码提交冲突的问题。
代码如下(示例):
//未遵守单一职责
public class Animal {
private String animal;
public Animal(String animal){
this.animal = animal;
}
public void run(){
System.out.println(this.animal + "用脚跑");
}
public static void main(String[] args) {
Animal dog = new Animal("dog");
Animal bird = new Animal("bird");
dog.run();
bird.run();
}
}
上述代码就违背了单一职责原则,Animal类被用于展示所有动物的活动方式,这显然是不合理的,对于用脚跑的当然合理,但是对于鸟类,一般来说鸟类使用翅膀飞行的。即使我们把run()方法改成用翅膀,也不合理。
修改方式大概有两种,一种是方法级的,就是不同运动方式的动物类别对应同一个方法。适用于方法较少时。
代码如下(示例):
//遵守单一职责(方法级修改)
public class Animal {
private String animal;
public Animal(String animal){
this.animal = animal;
}
public void footRun(){
System.out.println(this.animal + "用脚跑");
}
public void flyRun(){
System.out.println(this.animal + "用翅膀飞");
}
public static void main(String[] args) {
Animal dog = new Animal("dog");
Animal bird = new Animal("bird");
dog.footRun();
bird.flyRun();
}
}
另一种是把类拆开,代码比方法级复杂
代码如下(示例):
//遵守单一职责(类级修改)
public class Animal {
public static void main(String[] args) {
RunAnimal dog = new RunAnimal("dog");
FlyAnimal bird = new FlyAnimal("bird");
dog.footRun();
bird.flyRun();
}
}
class FlyAnimal{
private String animal;
public FlyAnimal(String animal){
this.animal = animal;
}
public void flyRun(){
System.out.println(this.animal + "用翅膀飞");
}
}
class RunAnimal{
private String animal;
public RunAnimal(String animal){
this.animal = animal;
}
public void footRun(){
System.out.println(this.animal + "用脚跑");
}
}
小结:
1. 单一职责可以降低类的复杂性
2.单一职责可以提高类的可读性和可维护性
3.单一职责可以降低类变更带来的风险
4.可以使用方法级和类级来实现遵守单一职责原则。
2.接口隔离原则
接口隔离原则是指一个类或接口对接口的依赖应建立在最小的接口上。这样把一个包含很多方法的接口拆成多个包含少量方法的小借接口,然后只需要将类与其相应的接口建立连接即可。这么做第一减少了很多不需要的方法实现。第二在原先的基础上,一旦接口新增了一个方法,任何实现其的类都要重写方法,无论是否需要,拆了以后,只有存在依赖关系的类才需要去实现。
示例代码:
// 未遵守接口隔离原则
interface DemoInterface{
void option1();
void option2();
void option3();
void option4();
void option5();
}
class Demo1 implements DemoInterface{
@Override
public void option1() {
System.out.println("real need");
}
@Override
public void option2() {
System.out.println("real need");
}
@Override
public void option3() {
System.out.println("real need");
}
@Override
public void option4() {
}
@Override
public void option5() {
}
}
class Demo2 implements DemoInterface{
@Override
public void option1() {
System.out.println("real need");
}
@Override
public void option2() {
}
@Override
public void option3() {
}
@Override
public void option4() {
}
@Override
public void option5() {
}
}
class Demo3 implements DemoInterface{
@Override
public void option1() {
}
@Override
public void option2() {
}
@Override
public void option3() {
}
@Override
public void option4() {
System.out.println("real need");
}
@Override
public void option5() {
System.out.println("real need");
}
}
// 遵守接口隔离原则
interface DemoInterface1{
void option1();
}
interface DemoInterface2{
void option2();
void option3();
}
interface DemoInterface3{
void option4();
void option5();
}
class Demo1 implements DemoInterface1,DemoInterface2{
@Override
public void option1() {
System.out.println("real need");
}
@Override
public void option2() {
System.out.println("real need");
}
@Override
public void option3() {
System.out.println("real need");
}
}
class Demo2 implements DemoInterface1{
@Override
public void option1() {
System.out.println("real need");
}
}
class Demo3 implements DemoInterface3{
@Override
public void option4() {
System.out.println("real need");
}
@Override
public void option5() {
System.out.println("real need");
}
}
3.依赖倒转原则(DIP)
依赖倒转原则是指高层模块不应该依赖低层模块,抽象不应该依赖具体,细节应该依赖抽象。其中心思想是面向接口编程。使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,具体实现细节由他们的实现类实现。
public class Main{
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Person{
public void receive(Email email){
System.out.println(email.getInfo());
}
}
class Email{
public String getInfo(){
return "Email Message: Hello World!";
}
}
很明显上述代码中Person类依赖了Email类。这样设计具有很大的局限性,因为现在能发信息的软件这么多,只让你用一个Email,这怎么能行呢。
有人说,这不很简单吗,我想用微信,那我就在新建一个微信类不就得了,那我们再来分析一下
public class Main{
public static void main(String[] args) {
Person person1 = new Person();
person1.receive(new Email());
Person person2 = new Person();
person2.receive(new Weixin());
}
}
class Person{
public void receive(Email email){
System.out.println(email.getInfo());
}
public void receive(Weixin weixin){
System.out.println(weixin.getInfo());
}
}
class Email{
public String getInfo(){
return "Email Message: Hello World!";
}
}
class Weixin{
public String getInfo(){
return "Weixin Message: Hello World!";
}
}
的确,上述代码实现了微信发送信息的功能,但是还有QQ呢,短信呢等等,难道我们要不断地去新建实体类,不断地去重载receive()方法吗?最好的方式就是遵循依赖倒转原则,我们利用接口帮我们完成对信息方式的分配,而具体的发送细节则由实现接口的类去完成,由接口(抽象)去约束细节。
public class Main{
public static void main(String[] args) {
Person person1 = new Person();
person1.receive(new Email());
Person person2 = new Person();
person2.receive(new Weixin());
}
}
interface IReceiver{
String getInfo();
}
class Person{
public void receive(IReceiver iReceiver){
System.out.println(iReceiver.getInfo());
}
}
class Email implements IReceiver{
public String getInfo(){
return "Email Message: Hello World!";
}
}
class Weixin implements IReceiver{
public String getInfo(){
return "Weixin Message: Hello World!";
}
}
DIP的三种方式
1. 接口实现(把接口作为参数传递到方法中)
上述代码就是接口实现就不在演示
2.构造器实现
public class Main{
public static void main(String[] args) {
Person person1 = new Person(new Email());
person1.receive();
Person person2 = new Person(new Weixin());
person2.receive();
}
}
interface IReceiver{
String getInfo();
}
class Person{
private IReceiver receiver;
public Person(IReceiver receiver){
this.receiver = receiver;
}
public void receive(){
System.out.println(this.receiver.getInfo());
}
}
class Email implements IReceiver{
public String getInfo(){
return "Email Message: Hello World!";
}
}
class Weixin implements IReceiver{
public String getInfo(){
return "Weixin Message: Hello World!";
}
}
3. setter方法实现
public class Main{
public static void main(String[] args) {
Person person1 = new Person();
person1.setReceiver(new Email());
person1.receive();
Person person2 = new Person();
person2.setReceiver(new Weixin());
person2.receive();
}
}
interface IReceiver{
String getInfo();
}
class Person{
private IReceiver receiver;
public void setReceiver(IReceiver receiver){
this.receiver = receiver;
}
public void receive(){
System.out.println(this.receiver.getInfo());
}
}
class Email implements IReceiver{
public String getInfo(){
return "Email Message: Hello World!";
}
}
class Weixin implements IReceiver{
public String getInfo(){
return "Weixin Message: Hello World!";
}
}
小结:
1.低层模块尽量都要由接口或者抽象类,或者二者都有,程序的稳定性更好
2.变量的声明类型尽量是抽象类或者接口类型,然后传入接口的实现类。有利于程序的拓展。
3.继承时遵循里式替换原则
4.里氏替换原则
里氏替换原则是针对类的继承性的。只一点要求,子类在继承父类时,不要轻易重写父类中的方法,即要求程序调用基类和调用子类产生的结果一致。如果非要重写方法,则希望通过聚合、依赖、组合等关系替代继承关系。
//未遵守里氏替换
public class Main{
public static void main(String[] args) {
Father father = new Father();
father.func1(3,2);
Son son = new Son();
son.func1(3,2);
}
}
class Father{
public void func1(int a, int b){
System.out.println(a-b);
}
}
class Son extends Father{
public void func1(int a, int b){
System.out.println(a+b);
}
}
通过泛化关系代替继承
public class Main{
public static void main(String[] args) {
A a = new A();
a.func1(3,2);
a.func2(3,2);
B b = new B();
b.func1(3,2);
b.func2(3,2);
}
}
class base{
public void func2(int a, int b){
System.out.println(a*b);
}
}
class A extends base{
public void func1(int a, int b){
System.out.println(a-b);
}
}
class B extends base{
public void func1(int a, int b){
System.out.println(a+b);
}
}
若B想用A的方法,可以使用组合的方式,即把A作为B的成员变量。
5.开闭原则(OCP)
开闭原则是编程中最基础、最重要的设计原则。
在编程中要考虑到程序的可拓展性,在新的需求出现时,要考虑用过扩展的行为来实现变化,而不是通过修改已有的代码去实现。
同时要求软件对提供方要开放,对使用方要闭合。就是对拓展开放,对修改关闭。
public class Main{
public static void main(String[] args) {
Choose choose = new Choose();
choose.choose(new Swim().type);
choose.choose(new Run().type);
choose.choose(new Fly().type);
}
}
class Choose{
public void choose(int type){
if(type == 1){
swim();
}else if(type == 2){
run();
}else{
fly();
}
}
public void swim(){
System.out.println("swim");
}
public void run(){
System.out.println("run");
}
public void fly(){
System.out.println("fly");
}
}
class Swim{
int type;
public Swim(){
type = 1;
}
}
class Run{
int type;
public Run(){
type = 2;
}
}
class Fly{
int type;
public Fly(){
type = 3;
}
}
这段代码就违反了OCP原则,当我们想要在增加一个走(walk)类型时,我们不得不修改原有的代码,增加实体类,增加判断类型。修改思路:抽取一个公共的接口,让这些实体类去实现接口,这样即使新增了运动方式,只需要实现接口而无需去增加判断类型。
public class Main {
public static void main(String[] args) {
Choose choose = new Choose();
choose.choose(new Swim());
choose.choose(new Run());
choose.choose(new Fly());
choose.choose(new Walk());
}
}
interface Demo{
void action();
}
class Choose{
public void choose(Demo demo){
demo.action();
}
}
class Swim implements Demo{
@Override
public void action() {
System.out.println("swim");
}
}
class Run implements Demo{
@Override
public void action() {
System.out.println("run");
}
}
class Fly implements Demo{
@Override
public void action() {
System.out.println("fly");
}
}
class Walk implements Demo{
@Override
public void action() {
System.out.println("walk");
}
}
6.迪米特法则
又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量把逻辑封装在类的内部。对外除了提供public方法外,不泄露其他任何信息。
话不多说看代码:
public class Main{
public static void main(String[] args) {
A a = new A();
B b = new B();
b.eat(a);
}
}
class A{
}
class B{
public void eat(A a){
System.out.println("I am B, i eat by myself");
System.out.println("he is A, he it by me, ridiculous!!!");
}
}
看到没有,A连吃饭都让B给嘲笑了,为什么呢,因为他不会自己吃饭!!!
public class Main{
public static void main(String[] args) {
A a = new A();
B b = new B();
b.eat(a);
}
}
class A{
public void eat(){
System.out.println("I am A, i can prove that i eat by myself and better than B!!!");
}
}
class B{
public void eat(A a){
System.out.println("I am B, i eat by myself");
a.eat();
}
}
怎么样,自己动手丰衣足食吧。
7.合成复用原则
这个原则一句话来说就是要求不变的代码与变化的代码不要放在一起,针对接口编程而不是面向细节编程。
总结
本篇幅通过文字与代码举例相结合简单的介绍了面向对象的七种设计模式。



