设计模式是什么?
设计模式是一套被使用、多数人知晓的、经过分类编目的、代码设计经验的总结,用于特定条件下为一些重复出现的软件设计问题提供合理的、有效的解决方案。
为什么要使用设计模式?
使用设计模式是为了可重用代码,让代码更容易被他人理解、保证代码可靠性。
设计模式的原则
1、单一职责原则:就一个类而言,应该仅有一个引起它变化的原因,它用于控制类的粒度大小,是实现高内聚、低耦合的指导方针。
2、里氏代换原则:继承必须要确保父类所拥有的性质在子类中仍然成立,子类参数的条件要比父类大,这样能用父类的地方就能用到子类,子类返回结果的类型要小于等于父类
3、依赖倒转原则:要针对接口编程,而不是针对实现编程。
4、接口隔离原则:使用多个专门的接口,而不是用单一的总接口。
5、迪米特法则:一个软件实体应当尽可能少的 与其他实体发生相互作用。迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达(出现在成员变量,方法的输入输出参数的类称为友元类,出现在方法体内部的类不属于友元类)
6、开闭原则:对扩展开放,对修改关闭。
7、合成复用原则:尽量使用对象组合,而不是继承来达到复用的目的。
设计模式的分类
设计模式分为5种创建型模式、七种结构型模式、十一种行为型模式。
创建型模式
单例模式
单例的目的是保证某个类仅有一个实例。当有某些创建对象开销较大时可以考虑使用该模式。单例模式又分为饿汉式,懒汉式,双检锁/双重校验锁,静态内部类,枚举
饿汉式
该模式在类被加载时(例如:直接进行new操作,访问静态属性,访问静态方法,用反射访问类,初始化一个类的子类等)就会实例化一个对象,线程安全
public class Person {
//饿汉式单例
private static Person person = new Person();
private Person(){}
public static Person getInstance(){
return person;
}
}
该模式能简单快速地创建一个单例对象,而且是 线程安全,但是不管要不要,都会创建一个对象,会消耗一定地性能。
懒汉式
该模式只在需要对象时才会生成单例模式,存在线程安全
public class User {
//懒汉式单例,只有在调用getInstance时才会实例化一个单例对象
private static User user;
private User(){
}
public static User getInstance(){
if(user==null){ //step 1.
user = new User(); //step 2
}
return user;
}
}
这段代码不是线程安全的,如果当前有n个线程同时调用getInstance()方法,由于当前还没有对象生成,所以一部分同时都进入step2,那么就会有多个线程创建多个user对象
双检锁/双重校验锁(DCL)
用来解决上边会创建多个user对象的情况,DCL机制不一定是线程安全的,因为有指令重排的存在,加入volatile可以禁止指令重排,线程安全
public class User {
private volatile static User user;
private static Integer key = new Integer(4);
private User(){
}
public static User getInstance(){
if(user == null) {//step1
synchronized (key) {
//由于可能多个线程都进入了step1,由于锁定机制,一个线程进入该代码块时,其他线程仍在排队进入该代码块,如果不做判断,当前线程即使创造了实例,下一个线程也不知道,就会继续创建一个实例
if(user==null){
user = new User();//不是一个原子性操作
/*
分配内存空间
执行构造方法,初始化对象
让这个对象指向这个空间
*/
//如果想变成原子性的,就给user前加volatile
}
}
}
return user;
}
}
静态内部类
这种方式能达到双检锁方式一样的功效,但实现更简单
public class Holder{
private Holder() {
}
public static Holder getInstance() {
return InnerClass.HOLDER;
}
public static class InnerClass {
private static final Holder HOLDER=new Holder();
}
}
枚举
这是实现单例模式的最佳方法,更简洁。自动支持序列化机制,绝对防止多次实例化。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
工厂模式
简单工厂模式
通过工厂来生产产品,而不是自己手动new,定义一个抽象对象(例如动物),然后定义一个工厂类,工厂类封装了生产具体动物的方法,然后通过参数传递给工厂类,使得用户不必关心对象是怎么生产的,甚至不用关心这个具体的对象是什么。
抽象产品
具体产品
具体工厂
抽象产品
public interface Shape {
void draw();
}
具体产品
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
具体工厂
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
调用该工厂,通过传递类型信息来获取实体类的对象
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
输出结果
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
工厂方法模式
在简单工厂中有一个缺点:如果我们想让工厂再能多生产一种东西,那么就需要修改这个工厂类的方法,,针对这个问题,引入了工厂方法模式,在工厂方法模式中,想要生产某一个品牌的鞋子,就使用其对应的工厂,也就是一个工厂只生产一种产品,如果想要有三种不同的产品,就要有三个工厂
工厂方法模式的主要角色:
抽象工厂:提供创建产品的接口
具体工厂:实现了抽象工厂中的抽象方法
抽象产品:定义了产品的规范,描述了产品的主要特征
具体产品:实现了抽象产品角色所定义的接口
首先创建一个抽象工厂
public interface CoffeeFactoryTest {
Coffee createCoffee();
}
再创建具体工厂
public class AmericanCoffeeFactory implements CoffeeFactoryTest{
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
public class LatteCoffeeFactory implements CoffeeFactoryTest{
@Override
public Coffee createCoffee() {
return new LatterCoffee();
}
}
创建抽象产品
public abstract class Coffee {
public abstract String getName();
public void addSuger() {
System.out.println("加糖");
}
public void addMilk() {
System.out.println("加奶");
}
}
创建具体产品
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
public class LatterCoffee extends Coffee {
@Override
public String getName() {
return "拿铁咖啡";
}
}
咖啡店
public class CoffeeStore {
private CoffeeFactoryTest coffeeFactoryTest;
public void setFactory(CoffeeFactoryTest coffeeFactoryTest) {
this.coffeeFactoryTest=coffeeFactoryTest;
}
public Coffee orderCoffee() {
Coffee coffee = coffeeFactoryTest.createCoffee();
coffee.addSuger();
coffee.addMilk();
return coffee;
}
}
测试
public class Client {
public static void main(String[] args) throws IOException {
CoffeeStore store=new CoffeeStore();
CoffeeFactoryTest coffeeFactoryTest=new AmericanCoffeeFactory();
store.setFactory(coffeeFactoryTest);
Coffee coffee=store.orderCoffee();
System.out.println(coffee.getName());
}
}
抽象工厂模式
工厂方法模式中存在一个缺点:一个工厂只能生产一种产品,比如鞋厂只能生产鞋,不能生产同一品牌的衣服,所以为了解决这个问题,就引出了抽象工厂,在抽象工厂类中定义多个产品,但是如果需要加产品的话,就需要对所有的具体工厂类都要改造
工厂模式考虑的是一类产品的生产,而抽象工厂考虑多等级产品的生产
抽象工厂
具体工厂
抽象产品
具体产品
抽象工厂
public interface DessertFactory {
Coffee createCoffee();
Dessert createDessert();
}
具体工厂
public class AmericanDessertFactory implements DessertFactory{
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
@Override
public Dessert createDessert() {
return new MatchaMousse();
}
}
public class ItalyDessertFactory implements DessertFactory{
@Override
public Coffee createCoffee() {
return new LatterCoffee();
}
@Override
public Dessert createDessert() {
return new Trimisu();
}
}
抽象产品
public abstract class Coffee {
public abstract String getName();
public void addSugar(){
System.out.println("加糖");
}
public void addMilk() {
System.out.println("加奶");
}
}
public abstract class Dessert {
public abstract void show();
}
具体产品
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
public class LatterCoffee extends Coffee {
@Override
public String getName() {
return "拿铁咖啡";
}
}
public class MatchaMousse extends Dessert{
@Override
public void show() {
System.out.println("抹茶慕斯");
}
}
public class Trimisu extends Dessert{
@Override
public void show() {
System.out.println("提拉米苏");
}
}
测试
public class Client {
public static void main(String[] args) {
// ItalyDessertFactory factory=new ItalyDessertFactory();
AmericanDessertFactory factory=new AmericanDessertFactory();
Coffee coffee = factory.createCoffee();
Dessert dessert=factory.createDessert();
System.out.println(coffee.getName());
dessert.show();
}
}
原型模式
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象
案例:同一学校的“三好学生”奖状除了获奖人姓名不同,其他都相同,可以使用原型模式复制多个“三好学生”奖状出来,然后再修改奖状上的名字即可
浅拷贝
clone默认的是浅拷贝
public class Citation implements Cloneable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Citation clone() throws CloneNotSupportedException {
return (Citation)super.clone();
}
public void show() {
System.out.println(name+"同学:成绩优秀,特发此证,以资鼓励");
}
}
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型
Citation citation=new Citation();
// 克隆奖状对象
Citation citation1=citation.clone();
citation.setName("XXXX");
citation1.setName("YYYY");
citation.show();
citation1.show();
}
}
最后的结果
XXXX同学:成绩优秀,特发此证,以资鼓励
YYYY同学:成绩优秀,特发此证,以资鼓励
若将上面的name属性修改为student类型的属性
public class Citation implements Cloneable {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
protected Citation clone() throws CloneNotSupportedException {
return (Citation)super.clone();
}
public void show() {
System.out.println(student.getName()+"同学:成绩优秀,特发此证,以资鼓励");
}
}
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型
Citation citation=new Citation();
Student student=new Student();
student.setName("张三");
citation.setStudent(student);
// 克隆奖状对象
Citation citation1=citation.clone();
citation1.getStudent().setName("李四");
citation.show();
citation1.show();
}
}
结果
李四同学:成绩优秀,特发此证,以资鼓励
李四同学:成绩优秀,特发此证,以资鼓励
这个就是浅拷贝造成的问题,stu对象和stu1对象是同一个对象,就会产生将stu1对象中name属性值改为”李四“,两个Citation对象显示的都是李四,所以对于具体原型中的引用类型的属性进行引用的复制,这种情况需要使用深拷贝。
大多是的深拷贝是利用Serializable的方式,这种方式代码量小,不容易出错
public class CitationTest {
public static void main(String[] args) throws Exception{
// 创建原型
Citation citation=new Citation();
Student student=new Student();
student.setName("张三");
citation.setStudent(student);
// 创建对象流输出流对象
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("C:\\Users\\lenovo\\Desktop\\config.txt"));
oos.writeObject(citation);
//释放资源
oos.close();
// 创建对象输入流
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("C:\\Users\\lenovo\\Desktop\\config.txt"));
// 读取对象
Citation citation1=(Citation)ois.readObject();
ois.close();
Student student1=citation1.getStudent();
student1.setName("李四");
citation.show();
citation1.show();
}
}
Citation和Student都需要实现序列化接口
建造者模式
将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的产品对象。
主要包含如下角色:
抽象建造者类:这个接口规定要实现复杂对象的那些部分的创建,不涉及具体的对象部分的创建
具体建造者类实现Builder接口,完成复杂产品的各个部件的具体创建方法,再构造过程完成后,提供产品的实例
产品类: 要创建的复杂对象
指挥者类: 调用具体构造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各个部分完整创建或按某种顺序创建
抽象建造者类
public abstract class Builder {
protected Bike bike=new Bike();
public abstract void buildFrame();
public abstract void buildSeat();
// 构建自行车的方法
public abstract Bike createBike();
}
具体建造者类
public class MobileBuilder extends Builder{
@Override
public void buildFrame() {
bike.setFrame("碳纤维车架");
}
@Override
public void buildSeat() {
bike.setSeat("真皮车座");
}
@Override
public Bike createBike() {
return bike;
}
}
public class OfoBuilder extends Builder {
@Override
public void buildFrame() {
bike.setFrame("铝合金车架");
}
@Override
public void buildSeat() {
bike.setSeat("橡胶车座");
}
@Override
public Bike createBike() {
return bike;
}
}
产品类
public class Bike {
// 车架
private String frame;
// 车座
private String seat;
public String getFrame() {
return frame;
}
public void setFrame(String frame) {
this.frame = frame;
}
public String getSeat() {
return seat;
}
public void setSeat(String seat) {
this.seat = seat;
}
}
指挥者类
public class Director {
private Builder builder;
public Director(Builder builder){
this.builder=builder;
}
// 组装自行车
public Bike construct() {
builder.buildFrame();
builder.buildSeat();
return builder.createBike();
}
}
模式扩展
当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码的可读性会非常差,很容易引入错误这个时候就可以利用建造者模式进行重构
public class Phone {
private String cpu;
private String screen;
private String memory;
private String mainboard;
private Phone(Builder1 builder){
this.cpu=builder.cpu;
this.screen=builder.screen;
this.memory=builder.memory;
this.mainboard=builder.mainboard;
}
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainboard='" + mainboard + '\'' +
'}';
}
public static final class Builder1 {
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Builder1 cpu(String cpu) {
this.cpu=cpu;
return this;
}
public Builder1 screen(String screen) {
this.screen=screen;
return this;
}
public Builder1 memory(String momory) {
this.memory=momory;
return this;
}
public Builder1 mainboard(String mainboard) {
this.mainboard=mainboard;
return this;
}
// 使用构建者创建phone对象
public Phone build() {
return new Phone(this);
}
}
}
public class Client1 {
public static void main(String[] args) {
// 创建手机对象
Phone phone=new Phone.Builder1()
.cpu("intel")
.screen("三星屏幕")
.memory("金士顿内存条")
.mainboard("华硕主板")
.build();
System.out.println(phone);
}
}
这样代码的可读性也比较强
工厂方法模式和建造者模式的区别
举个例子:如果要制造一个超人,使用工厂方法模式,直接产生出来的就是一个力大无穷,能够飞翔的超人,而使用建造者模式,则需要组装手,头,脚,躯干等部分,然后诞生一个超人
抽象工厂和建造者模式的区别
抽象工厂模式实现对产品家族的创建,一个产品家族是一系列的产品,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,主要目的是通过组装零配件而产生一个新产品
结构型模式
代理模式
由于某些原因需要给某对象提供一个代理以控制该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介
java按照代理类生成时机不同又分为静态代理和动态代理,静态代理代理类在编译期就生成,而动态代理代理类则是在java运行时动态生成,动态代理又有JDK代理和CGLIB代理两种
代理分为三种角色:
抽象主题: 声明真实主题和代理对象实现的业务方法
真实主题: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象
代理类: 提供了与真是主题相同的接口
静态代理
例如:如果要买火车票的话,需要去火车站买票,坐车等到火车站,排队等一系列的操作显然比较麻烦,而火车站在多个地方都有代售点,去代售点买票就方便很多了。这个例子就是代理模式,火车站是目标对象,代售点是代理对象。
抽象主题
//买火车票的接口
public interface SellTickets {
void sell();
}
真实主题
public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
代理类
//代售点
public class ProxyPoint implements SellTickets{
// 火车站类
private TrainStation trainStation=new TrainStation();
@Override
public void sell() {
System.out.println("代售点收取一些服务费用");
trainStation.sell();
}
}
客户端
public class Client {
public static void main(String[] args) {
//创建代售点类对象
ProxyPoint proxyPoint=new ProxyPoint();
proxyPoint.sell();
}
}
动态代理
jdk动态代理
创建个卖火车票的接口
//卖火车票的接口
public interface SellTickets {
void sell();
}
火车站实现了卖火车票接口
public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
创建一个代理工厂
public class ProxyFactory {
private TrainStation station=new TrainStation();
public SellTickets getProxyObject() {
SellTickets proxyObject = (SellTickets)Proxy.newProxyInstance(
station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println("invoke方法执行了");
System.out.println("代理点收取一定的费用(jdk动态代理)");
Object invoke = method.invoke(station, args);
return invoke;
}
}
);
return proxyObject;
}
}
newProxyInstance方法有三个参数,loader:需要代理谁,就用谁的类加载器,interface:动态代理需要实现的接口,也就是代理对象的接口,h:动态代理方法在执行时,会调用h里面的invoke方法执行
InvocationHandler中的invoke方法有三个参数,proxy:代理对象,一般不用,method:对接口中的方法进行封装,接口中的方法是sell(),所以这个method就是sell(),args:调用方法的实际参数,如果接口中的方法有参数的话,这个args就表示那些参数,如果没有,args就没有意义。还有这个invoke的方法的返回值,这个返回值就是接口方法的返回值,因为sell()方法没有返回值,所以返回的是null,
客户
public class Client {
public static void main(String[] args) {
// 获取代理对象
ProxyFactory factory=new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
jdk动态代理的原理
我们可以看到代理类继承了proxy类,并且实现了SellTickets接口,在代理类中将目标接口SellTickets的所有方法都以静态属性的形式保存起来了,在调用动态代理对象的某个方法时,实际上是调用Proxy类的h属性的invoke方法,而proxy.h其实就是创建对象时我们传入的InvocationHandler.
代理类($Proxy0) 实现了SellTickets。
代理类(Proxy0) 将提供的匿名内部类对象传给了父类
CGLIB动态代理
同样是卖火车票的案例,如果没有定义SellTickets接口,只定义了TrainStation,jdk动态代理就无法使用,因为jdk动态代理必须要求定义接口,CGLIB为没有实现接口的类提供代理,为JDKK的动态代理提供了很好的补充
首先导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
创建一个需要被代理的类
public class TrainStation {
public void sell() {
System.out.println("火车站卖票");
}
}
创建代理对象
//获取代理对象,也就是目标对象所属类的子类
public class ProxyFactory implements MethodInterceptor {
private TrainStation station=new TrainStation();
public TrainStation getProxyObject() {
// 创建Enhancer对象,类似于JDK代理中的proxy类
Enhancer enhancer=new Enhancer();
// 设置父类的字节码对象
enhancer.setSuperclass(TrainStation.class);
// 设置回调函数
enhancer.setCallback(this);
// 创建代理对象
TrainStation proxyObject=(TrainStation) enhancer.create();
return proxyObject;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代售点收取一定的费用(cglib)");
//调用火车站买票的方法
Object obj=method.invoke(station,objects);
return obj;
}
}
客户端调用
public class Client {
public static void main(String[] args) {
ProxyFactory factory=new ProxyFactory();
TrainStation proxyObject=factory.getProxyObject();
proxyObject.sell();
}
}
jdk代理和CGLIB代理的区别
使用CGLIB实现动态代理,CGLIB底层采用ASM字节码生成框架,使用字节码技术生成代理类。CGLIB不能声明为final的类或者方法进行代理,因为CGLIB原理是动态生成被代理类的子类。
动态代理和静态代理的对比
动态代理将接口中声明的所有方法都转移到调用处理器一个集中的方法中处理,这样在接口方法数量比较多的时候,可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
适配器模式
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类适配器模式和对象适配器模式,前者引用相对较少。
包含的角色:
目标接口:当前系统业务所期待的接口,可以是抽象类或者接口。
适配者模式:被访问和适配的现存组件库中的组件接口
适配器类:是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者
如图,壁式插座就是目标接口,标准的交流电插头就是适配者类,中间那个转换器是适配器类。
现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话,就需要使用到适配器模式,创建一个读卡器,将TF卡的内容读取出来
创建电脑类
//计算机类
public class Computer {
// 从SD卡中读取数据
public String readSD(SDCard sdcard) {
if(sdcard==null)
throw new NullPointerException("sd card is not null");
return sdcard.readSD();
}
}
创建SD卡接口
//目标接口
public interface SDCard {
// 往sd卡中读数据
String readSD();
// 往sd卡中写数据
void writeSD(String msg);
}
创建SD卡实现类
public class SDCardImpl implements SDCard {
@Override
public String readSD() {
String msg="SDCard read msg:hello SD";
return msg;
}
@Override
public void writeSD(String msg) {
System.out.println("SDCard write SD");
}
}
创建TF卡接口(适配者接口)
//适配者类的接口
public interface TFCard {
// 往TF卡里读取数据
String readTF();
// 往TF卡里写数据
void writeTF(String msg);
}
创建TF卡接口的实现类(适配者类的实现类)
public class TFCardImpl implements TFCard{
@Override
public String readTF() {
String msg="TFCard read msg:hello world";
return msg;
}
@Override
public void writeTF(String msg) {
System.out.println("TFCard write msg:"+msg);
}
}
创建适配器类
public class SDAdapterTF extends TFCardImpl implements SDCard{
//声明适配者类
private TFCard tfCard;
public SDAdapterTF(TFCard tfCard) {
this.tfCard=tfCard;
}
@Override
public String readSD() {
System.out.println("adapter read tf card");
return tfCard.readTF();
}
@Override
public void writeSD(String msg) {
System.out.println("adapter write tf card");
tfCard.writeTF(msg);
}
}
客户
public class Client {
public static void main(String[] args) {
// 创建计算机对象
Computer computer=new Computer();
// 读取SD卡的数据
String msg=computer.readSD(new SDCardImpl());
System.out.println(msg);
System.out.println("==========");
// 使用该电脑读取TF卡中的数据
// 创建适配器类对象
SDAdapterTF sdAdapterTF=new SDAdapterTF(new TFCardImpl());
String msg1=computer.readSD(sdAdapterTF);
System.out.println(msg1);
}
}
结果
SDCard read msg:hello SD
==========
adapter read tf card
TFCard read msg:hello world
装饰者模式
装饰者模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责的模式。缺点:装饰者模式会增加许多子类,过度使用会增加程序的复杂性。
比如快餐店里面有炒面,炒饭等这些快餐,可以额外附加鸡蛋,火腿,培根这些配菜
角色:
抽象构建角色:定义一个抽象接口以规范准备接收附加责任的对象。也就是快餐
具体构建角色:通过装饰角色,为其添加一些职责,也就是炒饭,炒面
抽象装饰角色:继承或者实现抽象构建角色,并包含具体构建实例,可以通过其子类扩展具体构建的功能
具体装饰角色: 实现抽象装饰的相关方法,并给具体构建对象添加附加责任,也就是鸡蛋,火腿
建立抽象构建角色
//快餐类,抽象构建角色
public abstract class FastFood {
// 价格
private float price;
// 描述
private String desc;
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public FastFood() {
}
public FastFood(float price, String desc) {
this.price = price;
this.desc = desc;
}
public abstract float cost();
}
建立具体构建角色
//炒面,具体构建角色
public class FriedNoodles extends FastFood{
public FriedNoodles() {
super(12,"炒面");
}
@Override
public float cost() {
return getPrice();
}
}
//炒饭,具体构建
public class FriedRice extends FastFood {
public FriedRice() {
super(10,"炒饭");
}
@Override
public float cost() {
return getPrice();
}
}
建立抽象装饰角色
//抽象装饰者角色
public abstract class Garnish extends FastFood {
// 声明快餐类的变量
private FastFood fastFood;
public FastFood getFastFood() {
return fastFood;
}
public void setFastFood(FastFood fastFood) {
this.fastFood = fastFood;
}
public Garnish(FastFood fastFood,float price,String desc) {
super(price,desc);
this.fastFood=fastFood;
}
}
建立具体装饰者
//具体装饰者
public class Egg extends Garnish {
public Egg(FastFood fastFood) {
super(fastFood,1,"鸡蛋");
}
@Override
public float cost() {
// 计算价格
return getPrice()+getFastFood().cost();
}
@Override
public String getDesc() {
return super.getDesc()+getFastFood().getDesc();
}
}
public class Bacon extends Garnish {
public Bacon(FastFood fastFood) {
super(fastFood,2,"培根");
}
@Override
public float cost() {
return getPrice()+getFastFood().cost();
}
@Override
public String getDesc() {
return super.getDesc()+getFastFood().getDesc();
}
}
建立客户端
public class Client {
public static void main(String[] args) {
FastFood food=new FriedRice();
System.out.println(food.getDesc()+" "+food.cost()+"元");
System.out.println("==========");
// 加一个鸡蛋
food=new Egg(food);
System.out.println(food.getDesc()+" "+food.cost()+"元");
System.out.println("=======");
// 再加一个鸡蛋
food=new Egg(food);
System.out.println(food.getDesc()+" "+food.cost()+"元");
System.out.println("======");
// 再加一个培根
food=new Bacon(food);
System.out.println(food.getDesc()+" "+food.cost()+"元");
}
}
行为型模式
模板方法模式
模板方法是定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
抽象模板
public abstract class HummerModel {
public abstract void start();
public abstract void stop();
public abstract void alarm();
//因为这个启动所有的类都是相同的,所以这里就不用abstract关键字了,如果用了每个子类都有相同
//的方法
public void run(){
this.start();
this.stop();
this.alarm();
};
}
具体模板
public class HummerModel1 extends HummerModel{
@Override
public void start() {
System.out.println("悍马1启动");
}
@Override
public void stop() {
System.out.println("悍马1停止");
}
@Override
public void alarm() {
System.out.println("悍马1鸣笛");
}
}
public class HummerModel2 extends HummerModel{
@Override
public void start() {
System.out.println("悍马2启动");
}
@Override
public void stop() {
System.out.println("悍马2停止");
}
@Override
public void alarm() {
System.out.println("悍马2鸣笛");
}
}
调用
public class Client {
public static void main(String[] args) {
HummerModel h1= new HummerModel1();
h1.run();
HummerModel h2 = new HummerModel2();
h2.run();
}
}
模板方法模式的优点
- 封装不可变部分,扩展可变部分:把认为是不可变的部分封装到父类实现,而可变的部分则可以通过继承来继续扩展
- 提取公共部分代码,便于维护
- 行为由父类控制,子类实现
策略模式
在策略模式中,一个类的行为或其算法可以在运行时更改,这种类型的设计模式属于行为型模式
观察者模式
当对象存在一对多关系时,则使用观察者模式,当一个对象被修改时,则会自动通知其他依赖它的对象,观察者模式属于行为型模式