Java笔记(20)常见设计模式
1.面向对象设计原则
Java是一门面向对象的语言,在实际开发中,为了能开发出更符合需求,更好兼容扩展性的程序,我们需要总结前人使用过的优秀的设计原则;
设计原则:
- 单一原则(高内聚,低耦合)就是说,每个类都只有一个职责,对外只提供一种功能,而引起该类的变化的原因只有一个;
- 开闭原则(扩展开放,修改关闭)它的体现是,修改类的功能只能通过增加代码来实现,而不是修改现有的代码;它的使用多是借用多态和抽象实现;
- 里式替换原则(子类替换)同一个继承体系中的对象应该有共同的行为特征;
- 依赖注入原则(依赖抽象)在应用程序中,当一个类对另一个类存在依赖关系时,这个类应该依赖的是另一个类的抽象类,而不是其具体实现类;
- 接口分离原则(功能分离)一个接口不需要提供太多的行为,不应该把所有的操作都封装在一个类中;
- 迪米特原则(降耦合)一个对象对其他对象应该尽可能少的了解,在模块之间,只使用接口编程,而不理会模块的内部工作原理;
这些思想原则需要在大量的代码练习和项目实战中加以深刻理解,而且在JDK源码中这些设计思想是最常见的,学习JDK源码也是一个非常好的学习设计模式和思想的方法;
2.设计模式(一)单例模式
所谓设计模式,就是一套被反复使用、多数人知晓的、经过分类编目的代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码的可靠性;设计模式并不是一种方法和技术,而是一种思想,和面向对象相辅相成。
单例模式
单例模式是一种常用的软件设计模式,它的核心结构只包含一个称为单例类的特殊类;通过单例模式可以保证系统中的一个类只有一个实例且该实例易于访问,从而方便控制实例个数并节约资源。如果你想要一个类只有一个对象存在,就使用该模式;
单例模式代码示例:
//单例模式类
public class Student {
//自己造一个Student对象,且不能被外界访问
private static Student s = new Student();
private String name;
private int age;
//私有化构造方法,使外界不能创建对象
private Student() {
}
//提供外界获得对象的方法
public static Student getStudent() {
return s;
}
//提供get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
Student s1 = Student.getStudent();
Student s2 = Student.getStudent();
System.out.println(s1 == s2);
System.out.println(s1);
System.out.println(s2);
s1.setName("李四");
s1.setAge(20);
s2.setName("张三");
s2.setAge(30);
System.out.println(s1.getName()+"---"+s1.getAge());
System.out.println(s2.getName()+"---"+s2.getAge());
}
}
//结果:
true
cn.test.Student@311d617d
cn.test.Student@311d617d
张三---30
张三---30
//显然,使用该模式创建的对象无论创建多少都是同一个对象
//上面的Student对象创建方式还可以采用另一种方式,也是单例模式:
public class Student {
//自己造一个Student对象,且不能被外界访问
private static Student s = null;
//私有化构造方法,使外界不能创建对象
private Student() {
}
//提供外界获得对象的方法
public static Student getStudent() {
if(s == null) {
s = new Student();
}
return s;
}
}
//这种模式属于延迟加载模式,但其可能会出现线程安全问题,例如多个线程同时访问该方法,可能会创建多个不同Student对象
//为了线程安全可以为该方法添加synchronized关键字
//第一种方式不会有线程安全问题,因为其不存在线程不安全的环境
3.设计模式(二)工厂模式
工厂模式主要是为创建对象提供一个接口,让其实现类决定其具体实例化哪一个类,并且由该实现类创建对应类的实例类;
1.简单工厂模式代码示例:
//面馆接口,提供做面方法
public interface Noodles {
public abstract void make();
}
//拉面类,做拉面的类,实现面馆接口
public class LmNoodles implements Noodles {
@Override
public void make() {
//做一份拉面
System.out.println("拉面来了!请问是否需要一份小菜呢?");
}
}
//方便面,做方便面的类
public class InstantNoodles implements Noodles {
@Override
public void make() {
System.out.println("你的泡面来了,请问是否需要加一包涪陵榨菜呢?");
}
}
//简单工厂类,专门创建不同面的对象
public class NoodlesFactory {
public static final int LM_Noodles = 1;
public static final int PM_Noodles = 2;
// 提供创建对象的工厂方法
public static Noodles creatNoodles(int noodles) {
switch (noodles) {
case LM_Noodles:
return new LmNoodles();
case PM_Noodles:
return new InstantNoodles();
default:
return new LmNoodles();
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//先来一份拉面
Noodles noodles1 = NoodlesFactory.creatNoodles(NoodlesFactory.LM_Noodles);
noodles1.make();
//再来一份泡面
NoodlesFactory.creatNoodles(NoodlesFactory.PM_Noodles).make();
}
}
简单工厂模式的类一般是一个具体类,只提供固定的可创建的对象,想要增加能创建的对象通常需要修改工厂类,其提供一个重要的creat方法,而且是静态的,也称为是静态工厂;
单例模式在JDK源码中也有很多的应用,例如Runtime类,该类是一个可以和其运行环境相连接的类,在Windows系统中可以通过该类下的exec方法直接调用系统的Dos命令,非常的方便。
2.工厂方法模式示例
//程序员抽象类,负责程序员的工作方法
public abstract class Programmer {
public abstract void work();
}
//总工厂,负责一个创建方法
public interface Factory {
//提供创建对象的方法
public abstract Programmer creatProgrammer();
}
//java程序员类,负责java工作
public class JavaProgrammer extends Programmer {
@Override
public void work() {
System.out.println("我是Java程序员,我的工作是开发Java程序");
}
}
//Java工厂,生产Java程序员
//Java程序员工厂
public class JavaFactory implements Factory {
@Override
public Programmer creatProgrammer() {
return new JavaProgrammer();
}
}
//汇编程序员类,负责汇编开发
public class AssemblyPorgrammer extends Programmer {
@Override
public void work() {
System.out.println("我是汇编程序员,我的工作是编写驱动");
}
}
//汇编程序员工厂
public class AssemblyFactory implements Factory {
@Override
public Programmer creatProgrammer() {
return new AssemblyPorgrammer();
}
}
//测试类
public class Demo {
public static void main(String[] args) {
//生产一个Java程序员
Factory f = new JavaFactory();
f.creatProgrammer().work();
}
}
如上就是一个工厂方法模式的代码示例,可以看出工厂方法模式有效的实现了工厂与数据分离,降低了耦合性。一个工厂类只负责一个对象的创建,从而明确了各个类的职责,在扩展新的工厂时,只需要新增加两个类即可,有效的提高了扩展性和可维护性,缺点是需要编写额外的代码,增加了工作量;
3.模板设计模式
模板方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现;
//定义抽象类模板
public abstract class MyGetTime {
public long getTime() {
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
return end - start;
}
public abstract void code();
}
//测试类
public class MyTest {
public static void main(String[] args) {
MyGetTime gt = new MyGetTime() {
@Override
public void code() {
for(int x = 0; x < 10000; x++) {
System.out.println(x);
}
}
};
//调用得到耗时的方法
System.out.println(gt.getTime() + "毫秒");
}
}
4.装饰模式
//面馆接口,提供做面的基本功能
public interface Noodle {
public abstract void makeNoodles();
}
//面馆实现类
public class WeNoodle implements Noodle {
@Override
public void makeNoodles() {
System.out.println("我的面馆可以做面");
}
}
//面馆装饰器,用来提供额外的功能,同时实现面馆接口
public abstract class NoodleDecorate implements Noodle {
//定义面馆对象
private Noodle n;
public NoodleDecorate(Noodle n) {
super();
this.n = n;
}
@Override
public void makeNoodles() {
n.makeNoodles();
}
}
//盖饭装饰实例类,使得面馆可以做盖饭了
public class CoverRiceDecorate extends NoodleDecorate {
public CoverRiceDecorate(Noodle n) {
super(n);
}
@Override
public void makeNoodles() {
super.makeNoodles();
makeCoverRice();
}
private void makeCoverRice() {
System.out.println("面馆可以做盖饭了");
}
}
//炒菜装饰器类
public class StirFryDecorate extends NoodleDecorate {
public StirFryDecorate(Noodle n) {
super(n);
}
@Override
public void makeNoodles() {
super.makeNoodles();
System.out.println("面馆可以做炒菜了");
}
}
public class Test {
public static void main(String[] args) {
//多态创建面馆对象
Noodle noodle = new WeNoodle();
//想让面馆做盖饭,将面馆对象传给装饰器
NoodleDecorate nd = new CoverRiceDecorate(noodle);
nd.makeNoodles();
System.out.println("-------------");
//不仅可以做盖饭,还可以做炒菜
nd = new CoverRiceDecorate(new StirFryDecorate(noodle));
nd.makeNoodles();
}
}
注意在上面的装饰模式中,装饰器类装饰的是一个Noodle面馆对象,而由于每个装饰器继承了总装饰器NoodleDecorate,而总装饰器又实现了Noodle面馆接口,所以本质上每个装饰器也是一个Noodle面馆对象,这就使得装饰器可以组合使用,如上面测试代码中的盖饭炒菜代码就使用了装饰器组合使用;
在JDK中的IO流中就有很多装饰模式的使用,例如BufferedReader类;