接口、工厂设计模式Factory、代理设计模式Proxy
接口定义
- 接口是一个特殊的类,接口空只能含有抽象方法和全局常量(在JDK 1.8 之后,接口中允许定义普通方法和静态方法,为解决子类过多,接口存在缺陷时,需覆写全部子类方法的问题。)
- 接口通过 interface 关键字定义
- 子类继承接口需要用 implements 关键字定义,继承多个接口,接口名之间用“逗号”隔开,实现多继承
- 接口的子类如果不是抽象类,则必须覆写接口的所有抽象方法
- 接口实例化通过子类向上转型
interface Person{
public abstract String eat();
public abstract String sleep();
}
class Student implements Person{
@Override
public String eat() {
return "学生吃饭...";
}
@Override
public String sleep() {
return "学生睡觉...";
}
}
class Teacher implements Person{
@Override
public String eat() {
return "教师吃饭...";
}
@Override
public String sleep() {
return "教师吃饭...";
}
}
public class JavaDemo {
public static void main(String[] args) {
Person perstu = new Student();
Person pertea = new Teacher();
System.out.println(perstu.eat());
System.out.println(perstu.sleep());
System.out.println(pertea.eat());
System.out.println(pertea.sleep());
}
}
学生吃饭...
学生睡觉...
教师吃饭...
教师吃饭...
Process finished with exit code 0
从实例看出,接口提供是一种操作标准,接口自身没有操作能力,需要通过子类去实现。
接口定义简化
由于接口的组成部分是抽象方法和全局常量,因此在 抽象方法 和 全局常量中,不需要写 public ,抽象方法也不需要使用 abstract 关键字定义,接口中所有的访问权限默认为public ,方法默认抽象方法。
所以上面实例中接口的定义可以这样简写。
interface Person{
String eat();
String sleep();
}
接口多继承
interface InterfaceA{}
interface IterfaceB{}
class classA implements InterfaceA,IterfaceB{}
子接口通过 extends 继承多个父接口
interface InterfaceA{}
interface IterfaceB{}
interface InterfaceC extends InterfaceA,IterfaceB{}
extends 关键字用于继承相同类型的类,implements 关键字用于继承接口
因此接口不能从抽象类继承。
工厂设计模式 Factory
- 工厂设计模式,就是建立一个工厂类,对实现了同一接口的所有子类进行统一的实例创建方法提供给用户调用。从而用户无需去了解这些对象该如何创建以及如何组织
- 有缺陷的设计模式:
客户端需要明确知道具体的哪一个子类,一旦子类增加,客户端也需要修改,此时程序出现耦合问题,造成耦合最直接的是关键字 new
package practicePackage;
interface Ifood{ // 建立一个食物的标准。可以食用就是食物
void eat();
}
class Bread implements Ifood{
public void eat(){
System.out.println("eat bread.");
}
}
class Milk implements Ifood{
public void eat(){
System.out.println("drink milk.");
}
}
public class JavaDemo {
public static void main(String[] args) {
Ifood food = new Bread(); // 这里每new一次,客户端都需要修改一次
food.eat();
}
}
- 工厂设计模式:
以JVM的设计为案例,JAVA具有可移植性的关键在于JVM,而JVM的核心原理就是利用一个虚拟机来运行,程序不直接与操作系统有依赖关系,而是通过JVM来进行匹配,所以良好的设计应该避免耦合。
package practicePackage;
interface Ifood{ // 建立一个食物的标准。可以食用就是食物
void eat();
}
class Bread implements Ifood{
public void eat(){
System.out.println("eat bread.");
}
}
class Milk implements Ifood{
public void eat(){
System.out.println("drink milk.");
}
}
class Factory{ // 工厂是一个中间商,避免客户端与子类直接接触。 因此增加子类,不需要修改客户端
public static Ifood getInstance(String clssName){
if("bread".equals(clssName)){
return new Bread();
}else if("milk".equals(clssName)){
return new Milk();
}else {
return null;
}
}
}
public class JavaDemo {
public static void main(String[] args) {
Ifood food = Factory.getInstance("bread");
food.eat();
}
}
// 运行结果
eat bread.
Process finished with exit code 0
上面的案例,定义了个Ifood接口,子类有 Bread、Milk,如果子类成千上万,那么就要创建成千上万个不同的new。所以需要定义一个工厂类 Factory,通过一个统一的方法去创建他们。
通过工厂模式,我们可以更加容易地管理多个相同接口的子类的操作。
代理设计模式
- 代理设计模式的只要功能是可以帮助开发者将所有的开发注意力只集中在核心业务功能的处理上。
- 代理设计模式的主要特点是:一个接口提供有两个子类,其中一个是真实业务操作类,另外一个是代理业务操作类,没有代理业务操作,真实业务无法进行。
- 代理设计模式,就是为一个具体对象提供一个代理对象,该代理对象主要是为了封装具体对象,并且完成与具体对象有关的所有其他操作。而具体对象则只需要负责核心业务。
以生活中的吃(eat)为例,需要先定义一个 eat 接口,然后一个具体类 WhatEat (用来指定具体吃什么东西),而在生活中,不管我们吃什么,都需要前置准备,比如:吃土豆烧排骨,要先买食材、处理食材、做菜,吃完了还要洗碗,这些都是代理对象来进行操作的。WhatEat 类中只负责核心*“吃”*的业务。
- 实现EatProxy代理节点类实现这些其他业务与WhatEat类操作。
package practicePackage;
interface Ieat{
int vegetable = 0; // 蔬菜
int meat = 1; // 肉
void eat(int type);
}
class WhatEat implements Ieat{
@Override
public void eat(int type){
if (type == Ieat.vegetable){
System.out.println("吃蔬菜...");
}else if (type == Ieat.meat){
System.out.println("吃肉...");
}
}
}
class EatProxy implements Ieat{
private Ieat eatObj; // 代理的具体对象
public EatProxy(Ieat eatObj){
this.eatObj = eatObj;
}
@Override
public void eat(int type) {
this.eatPrepare(type);
this.eatObj.eat(type);
this.eatFinish();
}
public void eatPrepare(int type){
if (type == Ieat.vegetable){
System.out.println("买蔬菜...");
System.out.println("处理食材...");
System.out.println("做菜、盛菜...");
}else if (type == Ieat.meat){
System.out.println("买肉...");
System.out.println("处理食材...");
System.out.println("整红烧肉...");
}
}
public void eatFinish(){
System.out.println("洗锅刷碗....");
}
}
public class JavaDemo {
public static void main(String[] args) {
Ieat proxy = new EatProxy(new WhatEat());
proxy.eat(Ieat.vegetable); // 通过代理节点吃蔬菜
System.out.println("---------");
proxy.eat(Ieat.meat); // 通过代理节点吃肉
}
}
// 运行结果
买蔬菜...
处理食材...
做菜、盛菜...
吃蔬菜...
洗锅刷碗....
---------
买肉...
处理食材...
整红烧肉...
吃肉...
洗锅刷碗....
Process finished with exit code 0
从上面案例可以看出,WhatEat 类只需要完成核心业务 “吃”,通过代理可以降低不同类之间的依赖性
抽象类与接口区别
区别 | 抽象类 | 接口 |
---|---|---|
关键字 | abstract | interface |
内部组成 | 支持属性,常量,构造方法,普通方法,abstract抽象方法,静态方法等 | 支持全局常量,abstract抽象方法 |
子类继承 | class 子类 extends 抽象类{} | class 子类 interface 接口1,接口2,…{} |
自身继承 | 抽象类可以继承于多个接口,或者抽象类. | 接口可以继承多个接口,但不能继承抽象类. |
继承限制 | 单继承,一个子类只能继承一个抽象类 | 多继承,一个子类可以继承多个接口 |