如果还是Java初入门的小白,推荐以后再来看哈,设计模式对于小白来讲还是有点抽象,记得给荔枝个关注、点赞和收藏等学成回来再看。
1.SOLID原则
在介绍设计模式之前,先来讲讲SOLID原则。
SOLID原则是面向对象设计中的一组指导原则,旨在提高代码的可维护性、可扩展性和可读性。
1.1. S(SRP,Single-Responsibility Principle,单一功能原则)
这条原则指出如何设计类,每个类都只应该有一个存在的理由。
讲人话就是你是个老师,你就应该教学生,你是个学生,你就该学习,就这意思。
1.2. O(Open-Closed Principle, OCP,开闭原则)
这条原则指出我们在设计类的时候必须要让它易于拓展,但是对修改是封闭的。
大概就是我自己本来的代码你不许改,想实现的话自己去扩展!
实现开闭原则的关键在于抽象和多态,通过定义抽象类或接口,将变化的部分抽象出来,让具体的实现类去实现这些抽象部分。
假如说我们在写人(Person)这个类的时候,没有给他定义说话(Speak)方法,那么学生和老师都应该继承Person嘛,毕竟都是人类
//代码示例 Person接口
public interface Person {}
//代码示例 Teacher类
public class Teacher implements Person{
public void speak() {
System.out.println("我是老师");
}
}
//代码示例 Student类
public class Student implements Person {
public void speak() {
System.out.println("我是个学生");
}
}
我们可以看到,老师和学生都实现了说话这个功能,但是Person类却没有提供,那么假如我想使用
List<Person> Personn = Arrays.asList(new Teacher(),new Student());
Personn.get(0).speak();// 显然会出错嘛,你这Person压根没有speak方法
上面这种情况就是没有充分考虑OCP原则导致的,只要我们回过头给Person加上speak方法就好了。这个例子告诉我们,如果不针对后续的扩展做一些设计,那么就得回头来更改当时的方案。
1.3. L(Liskov Substitution Principle, LSP,里氏替换原则)
里氏替换原则意味着与某个类相似的子类型也可以当作这个类来使用。
子类对象必须能够替换掉它们的父类对象,并且不破坏系统的正确性。
讲人话就是儿子可以顶替老父亲,把儿子当它爹用。
//Person类
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void introduce() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
}
//Teacher类
class Teacher extends Person {
private String subject;
public Teacher(String name, int age, String subject) {
super(name, age);
this.subject = subject;
}
@Override
public void introduce() {
super.introduce();
System.out.println("I am a teacher and I teach " + subject + ".");
}
}
//Student类
class Student extends Person {
private String grade;
public Student(String name, int age, String grade) {
super(name, age);
this.grade = grade;
}
@Override
public void introduce() {
super.introduce();
System.out.println("I am a student in grade " + grade + ".");
}
}
//Main
public class Main {
public static void main(String[] args) {
Person person = new Person("Alice", 30);
Person personTea = new Teacher("Bob", 40, "Math");//重点看这里,使用Person类创建的
Person personStu = new Student("Charlie", 15, "9th");
// 使用父类引用调用方法
person.introduce();
personTea.introduce();
personStu.introduce();
}
}
正确输出结果如下:
Hello, my name is Alice and I am 30 years old.
Hello, my name is Bob and I am 40 years old.
I am a teacher and I teach Math.
Hello, my name is Charlie and I am 15 years old.
I am a student in grade 9th.
1.4.I(Interface Segregation Principle,ISP,接口隔离原则)
要求某个类不应该依靠哪些虽然抽象了出来但是根本用不到的方法
比如说你给Person接口加了个倒立洗头的功能,但是人家老师和学生根本用不着实现这个功能,但是因为为了实现你这个接口,不得不实现你的倒立洗头功能。这对吗??这显然不对。
正确的方法该是我们把倒立洗头方法单独拉出来,再造一个接口invertedHairWashing,有谁需要实现这么个功能就去实现就好了。
1.5.D(Dependency Inversion Principle,DIP,依赖倒置原则)
高层模块不应依赖于低层模块,二者都应该依赖于抽象。
这条原则要求高层类(需要使用其他类来运作的类)不用去知道底层类,而是应该把它需要使用的底层类抽象成一个接口,让各种具体的底层类都去实现这个接口,这样高层类就可以转而去依赖这个接口了。
举个例子,假设我们设计一个驿站类,任何人都能到驿站对吧,我哪里管你是学生还是老师的,只要你有入住的能力(实现接口),是个人就能住。
//定义一个Enterable接口,表示可以进入驿站的能力
interface Enterable {
void enter(PostStation postStation);
}
//让Person、Teacher和Student类实现Enterable接口
class Person implements Enterable {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void enter(PostStation postStation) {
postStation.register(this);
System.out.println(name + " (Person) has entered the post station.");
}
}
class Teacher implements Enterable {
private String name;
public Teacher(String name) {
this.name = name;
}
@Override
public void enter(PostStation postStation) {
postStation.register(this);
System.out.println(name + " (Teacher) has entered the post station.");
}
}
class Student implements Enterable {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void enter(PostStation postStation) {
postStation.register(this);
System.out.println(name + " (Student) has entered the post station.");
}
}
//驿站类(PostStation)是一个高层类,它依赖于Enterable接口,而不是具体的Person及其子类
class PostStation {
private List<Enterable> visitors = new ArrayList<>();
public void register(Enterable enterable) {
visitors.add(enterable);
}
public void listVisitors() {
System.out.println("Current visitors in the post station:");
for (Enterable visitor : visitors) {
System.out.println(visitor);
}
}
}
1.6.第一部分小结
我们学习了SOLID这5个原则,事实上在开发当中完全坚持这些原则是很难做到的,而且这些原则无法保证你的代码不会变烂。
那怎么办?
哼哼,设计模式要出手了。
2.创建型设计模式
创建型设计模式(Creational Design Patterns)是一类专注于对象创建的设计模式。它们提供了一种在不直接指定对象具体类的情况下创建对象的方式,从而将对象的创建逻辑封装起来,提高代码的灵活性和可维护性。创建型设计模式主要解决的是“如何创建对象”的问题,同时隐藏创建逻辑,使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
2.1.工厂方法模式
这种模式主要是把创建某种类型的对象这一操作整到一个方法里,等程序真正运行的时候,这个方法决定来创建哪个类的实例。
你可以理解为整了一个工厂接口(大学),这个工厂有一个统一的培养毕业体系(下文的graduateStudent),有不同的学院都要照着这个毕业体系来,专门用来培养各种各样的人才
//人的接口
interface Person {
void introduce();
}
//工程师
class Engineer implements Person {
@Override
public void introduce() {
System.out.println("I am an Engineer. I design and build things.");
}
}
//医生
class Doctor implements Person {
@Override
public void introduce() {
System.out.println("I am a Doctor. I take care of people's health.");
}
}
//老师
class Teacher implements Person {
@Override
public void introduce() {
System.out.println("I am a Teacher. I educate students.");
}
}
//大学(工厂)
interface University {
Person graduateStudent();
}
//工程师学院
class EngineeringUniversity implements University {
@Override
public Person graduateStudent() {
return new Engineer();
}
}
//医学院
class MedicalUniversity implements University {
@Override
public Person graduateStudent() {
return new Doctor();
}
}
//师范学院
class NormalUniversity implements University {
@Override
public Person graduateStudent() {
return new Teacher();
}
}
//
public class Main {
public static void main(String[] args) {
// 创建工程学院
University engineeringUniversity = new EngineeringUniversity();
Person engineer = engineeringUniversity.graduateStudent();
engineer.introduce(); // 输出:I am an Engineer. I design and build things.
// 创建医学院
University medicalUniversity = new MedicalUniversity();
Person doctor = medicalUniversity.graduateStudent();
doctor.introduce(); // 输出:I am a Doctor. I take care of people's health.
// 创建师范学院
University normalUniversity = new NormalUniversity();
Person teacher = normalUniversity.graduateStudent();
teacher.introduce(); // 输出:I am a Teacher. I educate students.
}
}
University接口定义了一个工厂方法graduateStudent,用于创建Person对象,每个学院实现这个方法来实现不同graduateStudent
不过工厂方法模式有很多限制,比如它只适用于一个体系里的类,也就是说这些类必然有相同的特征(比如都是学院要培养人),如果有好几个系列则需要创建好几个工厂。
2.2.抽象工厂模式
抽象工厂模式也是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类,那他俩有啥区别呢?
工厂方法模式:主要用于创建单一类型的产品。它关注的是如何创建一个具体的产品对象,而不需要关心其他类型的产品。
通常涉及较少的接口和类。它主要有一个抽象工厂接口和一个抽象产品接口,以及对应的具体工厂和具体产品类。
例如,有一个抽象工厂VehicleFactory
,它有一个工厂方法createVehicle()
,具体工厂CarFactory
实现了这个方法,创建了Car
对象。
interface VehicleFactory {
Vehicle createVehicle();
}
class CarFactory implements VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Car();
}
}
interface Vehicle {
void drive();
}
class Car implements Vehicle {
@Override
public void drive() {
System.out.println("Driving a car");
}
}
抽象工厂模式:用于创建一族相关的产品。它关注的是如何创建一系列相关的产品对象,这些产品对象在逻辑上有一定的关联性。
它有一个抽象工厂接口,多个抽象产品接口,以及多个具体工厂和具体产品类。
例如,有一个抽象工厂ShapeColorFactory
,它有两个方法createShape()
和createColor()
,具体工厂CircleRedFactory
实现了这些方法,创建了Circle
对象和Red
对象
interface ShapeColorFactory {
Shape createShape();
Color createColor();
}
class CircleRedFactory implements ShapeColorFactory {
@Override
public Shape createShape() {
return new Circle();
}
@Override
public Color createColor() {
return new Red();
}
}
interface Shape {
void draw();
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
interface Color {
void fill();
}
class Red implements Color {
@Override
public void fill() {
System.out.println("Filling with red color");
}
}
2.3.建造者模式
它用于创建一个复杂的对象,同时允许用户只通过指定复杂对象的类型和内容就能构建它们,隐藏了复杂对象的构建细节
建造者模式的关键组成部分是充当构建者的类,含有建造产品的一些所需的一些值
具体建造者类根据指挥者的指令逐步构建复杂对象的各个部分。具体建造者类实现了抽象建造者接口中定义的方法,具体实现了如何构建复杂对象的各个组成部分,马上熄灯了,明天写哦!