常见的设计模式
- 简单工厂模式和工厂方法模式(接口)
- 模版设计模式(抽象类)
- 装饰者设计模式(IO流)
- 单例设计模式(多线程)
- 饿汉式
- 懒汉式
- 适配器设计模式
面向对象思想设计原则
单一职责原则
- 高内聚,低耦合
- 低耦合:让类与类之间的关系不复杂
- 高内聚:一个类能够完成的事情,不要使用多个类一起来执行!
- 每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则。
开闭原则
- 一个对象对扩展开放,对修改关闭。
- 对类的改动是通过增加代码进行的,而不是修改现有代码。
- 把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则是可以改变和扩展的。
里氏替换原则
- 在任何父类出现的地方都可以用它的子类来替代。
- 同一个继承体系中的对象应该有共同的行为特征。
依赖注入原则
要依赖于抽象,不要依赖于具体实现。
在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他类的具体类。
- 在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
接口分离原则
- 一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能。
- 不应该把所有的操作都封装到一个接口中。
迪米特原则
- 降低各个对象之间的耦合,提高系统的可维护性。
- 在模块之间应该只通过接口编程,而不理会模块的内部工作原理。
- 它可以使各个模块耦合度降到最低,促进软件的复用
简单工厂模式
又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例。
- 优点
- 客户端不需要再负责对象的创建,从而明确了个各类的职责 。
- 缺点
- 静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期维护。
/**
* 设置一个动物的抽象类
*/
abstract class Animal {
// 抽象方法
public abstract void eat();
}
/**
* 一个具体的动物类
*/
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
/**
* 设置一个动物类的工厂类
*/
class AnimalFactory {
// 无参构造私有化
private AnimalFactory() {
super();
}
public static Animal createAnimal(String type) {
if ("cat".equals(type)) {
return new Cat();
} else if ("dog".equals(type)) {
return new Dog();
} else {
return null;
}
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
// 通过工厂类创建对象
Animal a = AnimalFactory.createAnimal("cat");
a.eat();// 猫吃鱼
a = AnimalFactory.createAnimal("dog");
a.eat();// 狗吃肉
}
}
工厂方法模式
提供一个工厂接口(工厂接口),每一个具体的类都有对应的工厂类(实现工厂接口)。
具体对象的创建工作由继承抽象工厂的具体类实现。
- 优点:
- 客户端不需要在负责对象的创建,从而明确了各个类的职责,
- 如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码。
- 后期维护更容易,增强了系统的扩展性
- 弊端:
- 比起简单工厂模式代码量更大
/**
* 动物的抽象类
*/
abstract class Animal {
public abstract void eat();
}
/**
* 具体的动物类
*/
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
/**
* 工厂类的接口
*/
interface Factory {
// 返回值是动物的抽象类
public abstract Animal creatAnimal();
}
/**
* 生产猫的工厂类
*/
class CatFactory implements Factory {
@Override
public Animal creatAnimal() {
return new Cat();
}
}
/**
* 生产狗的工厂类
*/
class DogFactory implements Factory {
@Override
public Animal creatAnimal() {
return new Dog();
}
}
/**
* 工厂方法模式的测试类
*/
public class Test2 {
public static void main(String[] args) {
// 创建Factory对象
Factory f = new CatFactory();
Animal a = f.creatAnimal();
a.eat();// 猫吃鱼
f = new DogFactory();
a = f.creatAnimal();
a.eat();// 狗吃肉
}
}
单例模式
单例模式核心思想:
某些类的对象在创建的时候 ,在系统内存始终只存在一个对象。通过一个静态的方法去访问。
- 优点
- 在系统内存中只存在一个对象,因此可以节约系统资源。
- 对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
- 缺点
- 没有抽象层,因此扩展很难。
- 职责过重,在一定程度上违背了单一职责
单例模式分类:
- 饿汉式
- 懒汉式
设计思路:
- 定义个类,将该类的无参构造方法私有化。
- 在该类的成员位置创建该类对象 并且一定要私有化,防止外界更改这个对象。
- 在该类中提供静态成员方法(返回值就是创建的那个对象),能被当前类直接调用。
饿汉式:
- 在加载类的同时,对象的创建工作就完成了。
- 饿汉式不会出现问题,开发中常用。
class Student {
/**
* 无参构造私有化
*/
private Student() {
super();
}
// 成员位置创建实例
private static Student s = new Student();
/**
* 返回值是实例s
*/
public static Student getStudent() {
return s;
}
}
public class Test {
public static void main(String[] args) {
Student s1 = Student.getStudent();
Student s2 = Student.getStudent();
System.out.println(s1 == s2);// true
// s1和s2是同一个实例
}
}
懒汉式:
- 只在需要的时候才会创建对象的实例。
- 在多线程环境下存在多线程安全问题。
class Student {
// 私有化无参
private Student() {
super();
}
// 声明实例
private static Student s = null;
// 因为懒汉式加载存在延迟操作,当多个线程访问资源类时容易出错,需要添加同步
public synchronized static Student getStudent() {
if (s == null) {
s = new Student();
}
return s;
}
}
// 测试
public class Test {
public static void main(String[] args) {
Student s1 = Student.getStudent();
Student s2 = Student.getStudent();
System.out.println(s1 == s2);// true
}
}
当一个线程访问 if (s == null)
并判断为null,此时当前线程被阻塞,而第二个线程进来了,这样的话第二个线程创建了新的对象,那么第一个线程被唤醒的时候又创建多一个对象,这样在内存中就存在了两个对象。所以懒汉式必须添加同步锁。
案例
Runtime类
可以运行当前Java系统环境的程序,类似于系统中的运行程序,启动指定的进程。
import java.io.IOException;
public class RunTimeDemo {
public static void main(String[] args) throws IOException {
// 创建Runtime类的实例
Runtime r = Runtime.getRuntime();
// 开启某一个进程
r.exec("calc");// 启动计算器
r.exec("notepad");// 启动记事本
r.exec("mspaint");// 启动画图
}
}
Runtime类的源码
通过getRuntime()
方法返回了Runtime 类的实例,符合单列模式中的饿汉式。
public class Runtime {
private static final Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
}
装饰者设计模式
/**
* 手机的接口类
*/
interface Phone {
// 打电话的功能
void call();
}
/**
* 一个手机的实现类
*/
class Iphone implements Phone {
@Override
public void call() {
System.out.println("打电话");
}
}
/**
* 手机的装饰着抽象类
*/
abstract class PhoneDecoreate implements Phone {
private Phone p;
public PhoneDecoreate(Phone p) {
this.p = p;
}
@Override
public void call() {
this.p.call();//执行当前构造方法中传入的Phone p的call()方法
}
}
/**
* 装饰类的子实现类,提供彩铃功能
*/
class RingPhoneDecoreate extends PhoneDecoreate {
public RingPhoneDecoreate(Phone p) {
super(p);
}
@Override
public void call() {
System.out.println("彩铃");
super.call();//这里又回到父类PhoneDecoreate中执行父类的call()方法
}
}
/**
* 装饰类的子实现类,提供打电话之后听音乐的功能
*/
class MusicPhoneDecoreate extends PhoneDecoreate{
public MusicPhoneDecoreate(Phone p) {
super(p);
}
@Override
public void call() {
super.call();
System.out.println("听音乐");
}
}
public class Test {
public static void main(String[] args) {
PhoneDecoreate p = new RingPhoneDecoreate(new Iphone());
p.call();
/*彩铃
打电话*/
p = new MusicPhoneDecoreate(new Iphone());
p.call();
/*打电话
听音乐*/
p = new RingPhoneDecoreate(new MusicPhoneDecoreate(new Iphone()));
p.call();
/*彩铃
打电话
听音乐*/
}
}
装饰者设计模式设计原则:
- 首先需要有一个抽象者装饰类;
- 每一个装饰类都继承自抽象者装饰类;
- 装饰类在执行新添加的功能后会返回执行抽象者装饰类中相同的方法;
例题分析
对于以下代码:
PhoneDecoreate p = new RingPhoneDecoreate(new MusicPhoneDecoreate(new Iphone()));
1、通过多态的形式创建了PhoneDecoreate(抽象者装饰类)
的实例。
2、通过p执行p.call();
的时候先访问RingPhoneDecoreate
中的call()
方法
//RingPhoneDecoreate中的call()方法
public void call() {
System.out.println("彩铃");
super.call();
}
3、输出彩铃
后super.call();
又回到父类PhoneDecoreate
中执行父类的call()
方法。
abstract class PhoneDecoreate implements Phone {
private Phone p;
public PhoneDecoreate(Phone p) {
this.p = p;
}
@Override
public void call() {
this.p.call();
}
}
4、父类中执行的是当前构造方法中传入的对象的call()
方法,而此时构造方法中是new MusicPhoneDecoreate
于是就进入MusicPhoneDecoreate
中执行其中的call()
方法。
class MusicPhoneDecoreate extends PhoneDecoreate {
public MusicPhoneDecoreate(Phone p) {
super(p);
}
@Override
public void call() {
super.call();
System.out.println("听音乐");
}
}
5、MusicPhoneDecoreate
中的call()
方法先执行了super.call();
又返回到父类中,此时MusicPhoneDecoreate
的构造方法中传入的是Iphone
的实例,于是先执行了Iphone
中的call()
方法。
class Iphone implements Phone {
@Override
public void call() {
System.out.println("打电话");
}
}
6、输出打电话
之后回到MusicPhoneDecoreate
中又执行了System.out.println("听音乐");
输出了听音乐
。
7、所以最后的结果是
装饰着设计模式的优点
当我们需要对现有的类添加新的功能的时候只需创建新的装饰者类即可,不需要修改已有的代码,符合开闭原则。