设计模式
面对对象程序设计原则
单一职责原则(single responsibility principle)
——优化代码的第一步
- 小明写的图片加载框架中将各个功能拆分,将Image’Loader一分为二,ImageLoader只负责图片的加载逻辑,ImageCache只负责图片的缓存逻辑;这样ImageLoader的代码量少了,逻辑也清晰了
开闭原则(open close principle)
——让程序更稳定、更灵活
- 软件中的对象(类、模块、函数)应该对扩展是开放的,但是对修改是封闭的。而遵守开闭原则的重要手段应该时通过抽象…;开闭原则指导我们,当软件需要变化时,应该“尽量”通过扩展的方式来实现变化,而不是修改已有的代码; 并不是绝对的,现实场景中会有修改源码的情况。
小明的代码通过依赖注入(set方法)的方式设置缓存类型,比如内存缓存、sd卡缓存、双缓存;ImageLoader中声明Cache的接口类,set方法设置的对象都是Cache的子类;也就是多态;这样修改后,如果后期有其他缓存方式需要修改,完全不需要对源码修改,只需要添加一个新的Cache的实现类就可以,也就是对扩展开放,对修改是封闭的。
里氏替换原则(Liskov Substitution Principle)
——构建扩展性更好的系统
依赖继承、多态这两大特性,简单的说就是任何使用父类的地方都可以使用子类代替;
上面小明通过提取Cache接口,使用依赖注入的方法设置缓存类型,传入Cache的子类(内存、sd卡、双缓存)的这种方式就是里氏替换原则。
依赖倒置原则(Dependence Inversion Principle)
——让项目拥有变化的能力
是指一种特定的解耦形式,是指高层次的模块不依赖于低层次的模块的实现细节的目的,依赖模块被颠倒了。
上面小明通过提取Cache接口,使用依赖注入的方法设置缓存类型,传入Cache的子类(内存、sd卡、双缓存)的这种方式就是里氏替换原则;并且这样的话不会像修改前期那样,在ImageLoader中直接创建ImageCache对象,ImageLoader过度依赖低层次模块(ImageCache);将其抽取成接口,使用依赖注入的方式,这样就是依赖倒置原则。
接口隔离原则(InterfaceSegregation Principles)
——系统有更高的灵活性
定义:不要强制破坏程序实现不想实现的原则;
目的:系统解开耦合,从而容易重构、更改和重新部署
小明设计的ImageLoader中的ImageCache就是接口隔离原则的运用,ImageLoader只需要知道该缓存对象有存、取缓存图片的接口即可,其他一概不管,这就使得缓存功能的具体实现对ImageLoader隐藏。这就是用最小化接口隔离了实现类的细节,也促使我们将庞大的接口拆分到更细粒度的接口当中,这使得我们的系统具有更低的耦合性,更高的灵活性。
迪米特原则(Law of Demeter)
——更好的扩展性
定义:一个类应该对自己需要耦合或者调用的类知道的最少,类的内部如何实现与调用者或者依赖者没关系,调用者或者依赖着只需要知道它需要的方法即可,其他的一概不用管。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另外一个类的影响也越大。——高内聚,低耦合
案例:房屋、中介、租客;改前 租客通过中介获取房屋资源,自己筛选符合条件的房屋;修改后租客直接调用中介的找房方法(传输价格和空间条件),中间根据条件找到合适的房屋返给租客就ok;
设计模式概述
- 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编写、代码设计经验的总结。
- 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性以及代码的结构更加清晰.
设计模式分类
创建型模式(创建对象的): 单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式;
行为型模式(对象的功能): 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式;
结构型模式(对象的组成): 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
简单工厂模式
- 简单工厂模式概述:举个例子,我现在有一个手机对象,它里面有构造方法也有打电话方法,但是,这样设计不好,又要创建该对象,又要调用功能,应该把创建对象分离出来;
- 简单工厂模式又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例;
- 优点: 使用静态工厂模式的优点是实现责任的分割,该模式的核心是工厂类,工厂类含有必要的选择逻辑,可以决定什么时候创建哪一个产品的实例,而客户端则免去直接创建产品的责任,而仅仅是消费产品。也就是说静态工厂模式在不改变客户端代码的情况可以动态的增加产品,明确了类的职责;
- 缺点:这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护;
public class test{
public static void main(String[] args) {
Animal cat = AnimalFactory.creatAnimal("cat");
cat.eat();
cat=AnimalFactory.creatAnimal("Dog");
cat.eat();
}
}
//执行结果:
//cat eat fish
//dog eat shit
class AnimalFactory {
public static Animal creatAnimal(String name) {
if ("cat".equals(name)) {
return new Cat();
}
if ("Dog".equals(name)) {
return new Dog();
}
if ("Tiger".equals(name)) {
return new Tiger();
}else return null;
}
}
abstract class Animal{public abstract void eat();}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("dog eat shit");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
class Tiger extends Animal{
@Override
public void eat() {
System.out.println("Tiger eat cat");
}
}
工厂模式
工厂模式概述:该模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现;
- 优点:客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性;
- 缺点: 需要额外的编写代码,增加了工作量;
public class test{
public static void main(String[] args) {
Animal Dog = new DogFactory().createAnimal();
Dog.eat();
Animal Cat = new CatFactory().createAnimal();
Cat.eat();
}
}
//执行结果:
//dog eat shit
//cat eat fish
abstract class Animal{public abstract void eat();}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("dog eat shit");
}
}
interface BigFctory{
//接口需要实现返回一个Animal类型的创建动物的方法
Animal createAnimal();
}
class DogFactory implements BigFctory{
@Override
public Animal createAnimal() {
return new Dog();
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
class CatFactory implements BigFctory{
@Override
public Animal createAnimal() {
return new Cat();
}
}
单例模式
- 保证一个类的对象在内存中只有一个,不能创建很多对象;
- 首先我们必须私有对象的构造,然后在该对象所在的类内提供静态方法,以便于我们获取该类的对象;
1、懒汉式
public class test{
public static void main(String[] args) {
System.out.println(Student.stu);//null
Student student = Student.getStudent();
Student student1 =Student.getStudent();
System.out.println(student==student1);//true
System.out.println(Student.stu);//Student@4554617c
}
}
class Student {
public static Student stu=null;
private Student() {
}
//synchronized 保证多线程环境下也为创建一个对象
public synchronized static Student getStudent() {
if (stu == null) {
stu = new Student();
}
return stu;
}
}
2、饿汉式
public class test{
public static void main(String[] args) {
System.out.println(Student.stu);//Student@4554617c
Student student = Student.getStudent();
Student student1 =Student.getStudent();
System.out.println(student==student1);//true
System.out.println(Student.stu);//Student@4554617c
}
}
class Student {
public static Student stu=new Student();
private Student() {
}
public static Student getStudent() {
return stu;
}
}
实际开发中我们使用饿汉式;Java中有一个类,Runtime,他就使用了饿汉式;
这个类可以执行一些DOS命令;
模板设计模式
模版设计模式概述:就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现;
优点: 使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求;
缺点: 如果算法骨架有修改的话,则需要修改抽象类;
需求: 计算一个方法所执行的时间
public class test{
public static void main(String[] args) {
//使用父类接收 继承父类的方法
CalcClass calcFor = new CalcFor();
//调用父类的方法
calcFor.calc();
}
}
abstract class CalcClass{
public void calc(){
long start=System.currentTimeMillis();
calcMethod();
long end=System.currentTimeMillis();
System.out.println("耗时"+(end-start)+"毫秒");
}
public abstract void calcMethod();
}
class CalcFor extends CalcClass {
@Override
public void calcMethod() {
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
}
}
装饰者设计模式
概述: 装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类,是继承的替代方案;
优点:使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能。
缺点: 正因为可以随意组合,所以就可能出现一些不合理的逻辑。
package test;
interface Phone{
void call();
}
class Iphone implements Phone {
@Override
//定义一个基础手机类IPhone
public void call() {
System.out.println("打电话");
}
}
class BZphone implements Phone{
private Phone phone;
//传入需要增加功能的基本手机类来进行包装,包装手机类是一个平台,用于其他功能手机类去在基础上增加功能。
public BZphone(Phone phone) {
this.phone = phone;
}
@Override
public void call() {
//继承接口的重写call方法为传入的基本手机类实现的call方法
this.phone.call();
}
}
class MusicPhone extends BZphone{
public MusicPhone(Phone phone) {
super(phone);
}
@Override
public void call() {
//在基本手机类的call功能上再增加自己的听音乐功能
super.call();
System.out.println("增加听音乐的功能");
}
}
class GamePhone extends BZphone{
public GamePhone(Phone phone) {
super(phone);
}
@Override
public void call() {
//在基本手机类的call功能上再增加自己的game功能
super.call();
System.out.println("增加玩游戏的功能");
}
}
class VideoPhone extends BZphone{
public VideoPhone(Phone phone) {
super(phone);
}
@Override
public void call() {
//在基本手机类的call功能上再增加自己的Video功能
super.call();
System.out.println("增加视频的功能");
}
}
测试类:
package test;
public class test {
public static void main(String[] args) {
Iphone iphone = new Iphone();
iphone.call();
System.out.println("============================");
//先增加音乐功能再增加游戏功能
GamePhone gamePhone = new GamePhone(new MusicPhone(iphone));
gamePhone.call();
System.out.println("============================");
//也可以先增加游戏功能再增加音乐功能
MusicPhone musicPhone = new MusicPhone(new GamePhone(iphone));
musicPhone.call();
System.out.println("============================");
VideoPhone videoPhone = new VideoPhone(new GamePhone(new MusicPhone(iphone)));
videoPhone.call();
/*打电话
============================
打电话
增加听音乐的功能
增加玩游戏的功能
============================
打电话
增加玩游戏的功能
增加听音乐的功能
============================
打电话
增加听音乐的功能
增加玩游戏的功能
增加视频的功能*/
}
}
观察者设计模式
观察者 = 订阅者 + 发布者;
案例: 找猎头找工作
岗位类 求职者 猎头(注册方法,注销方法,发布方法)
岗位类
package test;
public class Job {
private String JobName;
private Double Sal;
public Job(String jobName, Double sal) {
JobName = jobName;
Sal = sal;
}
public Job(){};
public String getJobName() {
return JobName;
}
public void setJobName(String jobName) {
JobName = jobName;
}
public Double getSal() {
return Sal;
}
public void setSal(Double sal) {
Sal = sal;
}
}
求职者类
package test;
public class Worker {
private String name;
public Worker(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
猎头类
package test;
import java.util.ArrayList;
public class Hunter {
private ArrayList<Worker> workersList= new ArrayList<>();
private ArrayList<Job> JobsList= new ArrayList<>();
//注册求职者的方法
public void AddWorker(Worker worker){
workersList.add(worker);
System.out.println(worker.getName()+"注册成功!");
}
//注销求职者的方法
public void RemoveWorker (Worker worker){
workersList.remove(worker);
System.out.println(worker.getName()+"注销成功! ");
}
//注册工作岗位并通知所有已经注册的求职者前来应聘的方法
public void AddJob(Job job){
JobsList.add(job);
NotifyWorker(job);
}
private void NotifyWorker(Job job){
for (Worker worker : workersList) {
System.out.println("hi"+worker.getName()+"这里有一份工作: "+job.getJobName()+" "+job.getSal()+"等待您前来面试");
}
}
}
测试类
package test;
public class test1 {
public static void main(String[] args) {
Worker s1 = new Worker("张三1");
Worker s2 = new Worker("张三2");
Worker s3 = new Worker("张三3");
Hunter hunter = new Hunter();
hunter.AddWorker(s1);
hunter.AddWorker(s2);
hunter.AddWorker(s3);
Job javaJob = new Job("Java工程师", 10000.0);
hunter.AddJob(javaJob);
System.out.println("===========================");
hunter.RemoveWorker(s1);
Job CJob = new Job("C++工程师", 10000.0);
hunter.AddJob(CJob);
/*张三1注册成功!
张三2注册成功!
张三3注册成功!
hi张三1这里有一份工作: Java工程师 10000.0等待您前来面试
hi张三2这里有一份工作: Java工程师 10000.0等待您前来面试
hi张三3这里有一份工作: Java工程师 10000.0等待您前来面试
===========================
张三1注销成功!
hi张三2这里有一份工作: C++工程师 10000.0等待您前来面试
hi张三3这里有一份工作: C++工程师 10000.0等待您前来面试*/
}
}