古怪的泛型循环 CRG
他能产生使用子类作为参数 和 返回类型 的父类
class BasicHolder<T>{
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
void f(){
System.out.println(item.getClass().getSimpleName());
}
}
class SubType extends BasicHolder<SubType>{}
public class Holder {
public static void main(String[] args) {
SubType s1 = new SubType(), s2 = new SubType();
s1.setItem(s2);
SubType s3 = s1.getItem();
s1.f();
}
}
SubType
基类用子类替代其参数,因此在SubType中,传递给setItem()和getItem()的返回类型都是确切的SubType。
混型
混型最基本的概念是混合多个类的能力,以产生一个可以表示混型中所有类型的类
混型与接口相结合
例如 现在需要把获取当前时间戳、获取计数器和存取某个值的三个类合成一个混型。
interface TimeStamped {
Date getStamp();
}
class TimeStampedImp implements TimeStamped {
private final Date timeStamp;
public TimeStampedImp() {
timeStamp = new Date();
}
@Override
public Date getStamp() {
return timeStamp;
}
}
interface SerialNumbered {
long getSerialNumber();
}
class SerialNumberedImp implements SerialNumbered {
private static long counter = 1;
private final long serialNumber = counter++;
@Override
public long getSerialNumber() {
return serialNumber;
}
}
interface Basic {
public void set(String val);
public String get();
}
class BasicImp implements Basic {
private String value;
@Override
public void set(String val) {
value = val;
}
@Override
public String get() {
return value;
}
}
//自己创建的混型
interface Game{
String lol();
}
class GameImp implements Game{
private String str;
public GameImp() {
str = "来把LOL";
}
@Override
public String lol(){
return str;
}
}
//实现获取时间 和 序列号
class Mixin extends BasicImp implements TimeStamped, SerialNumbered ,Game{
private TimeStamped timeStamp = new TimeStampedImp();
private SerialNumbered serialNumber = new SerialNumberedImp();
private Game str = new GameImp();
@Override //每加入一个混型就要重写方法,代码量增大
public Date getStamp() {
return timeStamp.getStamp();
}
@Override
public long getSerialNumber() {
return serialNumber.getSerialNumber();
}
@Override
public String lol() {
return str.lol();
}
}
public class Mixins {
public static void main(String[] args) {
Mixin mixin1 = new Mixin(), mixin2 = new Mixin();
mixin1.set("测试1");
System.out.println(mixin1.get() + " " +
mixin1.getStamp() + " " + mixin1.getSerialNumber()+" "+mixin1.lol());
mixin2.set("测试2");
System.out.println(mixin2.get() + " " +
mixin2.getStamp() + " " + mixin2.getSerialNumber());
}
}
测试1 Mon May 16 08:41:31 CST 2022 1 来把LOL
测试2 Mon May 16 08:41:31 CST 2022 2
Mixin 类基本上在使用代理,因此每个混入类型都要求在Mixin 中有一个相应的域,还必须在Mixin 中编写所有必须的方法。 这样下来当 使用更复杂的混型时,代码数量会急速增加。
通过动态代理来实现混型
由于动态代理的限制,每个被混入的类都要求为接口。
public class TwoTuple<A, B> {
public TwoTuple(A first, B second) {
this.first = first;
this.second = second;
}
private A first;
private B second;
public A getFirst() {
return first;
}
public B getSecond() {
return second;
}
}
class MixinProxy implements InvocationHandler {
// 在 MixinProxy 代理类的构造方法中,将需要代理的方法作为 key ,
// 实现代理方法的实现类作为 value 存入 delegatesByMethod 中。
private Map<String, Object> delegatesByMethod;
//TwoTuple 存储两个类型参数,第一个为被代理接口的实现类,第二个为被代理接口。
public MixinProxy(TwoTuple<Object, Class<?>> ... pairs) {
delegatesByMethod = new HashMap<String, Object>();
for (TwoTuple<Object, Class<?>> pair : pairs) {
//getMethods(): 返回一个包含Method对象的数组,
// 该数组反映由这个class对象表示的类或接口的所有公共方法
for (Method method : pair.second.getMethods()) {
//获取要代理接口中所有的Method对象
//method.getName(): 返回由这个method对象表示的方法的名称,作为一个String。
String methodName = method.getName();
//System.out.print(methodName+" ");
//判断是否包含将要代理的接口中的方法 不包含的话 将Method与实现类对应
//即动态绑定 Map中保存的是每一个具体实现类与将要代理的方法一一对应
//将需要代理的方法作为 key ,实现代理方法的实现类作为 value 存入 delegatesByMethod 中。
if (!delegatesByMethod.containsKey(methodName))
delegatesByMethod.put(methodName, pair.first);
}
}
}
//当指定的接口类的方法被调用时 真正的调用发生在此处 根据调用的方法 去Map中查找对于的具体实现类 然后通过反射调用
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Object delegate = delegatesByMethod.get(methodName);
return method.invoke(delegate, args);
}
@SuppressWarnings("unchecked")
//在此处处理多个接口与多个实现类的关系 产生真正的代理对象
//通过 newInstance() 方法获取实例时,传入第一个接口实现类的类加载器、需要被代理的接口、实现 InvocationHandler 接口的类
// (任意一个接口实现类的加载器都行,传入第一个是防止因下标越界引起异常)
//返回代理对象
public static Object newInstance(TwoTuple ... pairs) {
Class[] interfaces = new Class[pairs.length];
for (int i = 0; i < pairs.length; i++) {
interfaces[i] = (Class) pairs[i].second;
}
//传入第一个接口实现类的类加载器
ClassLoader cl = pairs[0].first.getClass().getClassLoader();
return Proxy.newProxyInstance(cl, interfaces, new MixinProxy(pairs));
}
}
public class DynamicProxyMixin {
public static void main(String[] args) {
//因代理对象同时代理了多组接口,
// 故可以转换为 Basic 、TimeStamped 、SerialNumbered 任意类型进行方法调用。
Object mixin = MixinProxy.newInstance(
tuple(new BasicImp(), Basic.class),
tuple(new TimeStampedImp(), TimeStamped.class),
tuple(new SerialNumberedImp(), SerialNumbered.class),
tuple(new GameImp(), Game.class)); //现在加入一个游戏类
Basic b = (Basic) mixin;
TimeStamped t = (TimeStamped) mixin;
SerialNumbered s = (SerialNumbered) mixin;
b.set("Hello");
System.out.println(b.get());
System.out.println(t.getStamp());
System.out.println(s.getSerialNumber());
System.out.println(g.lol()); //调用游戏类方法
}
}
输出:
Hello
Mon May 16 09:04:06 CST 2022
1
来把LOL
倘若我们需要添加新的类型至混型之中,只需要拓展对应的接口。相比直接通过接口实现混型,此方式的代码量更少,也更为灵活
装饰器模式
装饰器是通过使用 组合 和 形式化结构 (可装饰物/装饰器层次结构)来实现的,而混型是基于继承的。
因此可以将基于参数化类型的混型当作是一种泛型装饰器机制 。 这种机制不需要装饰器设计模式的继承结构
—————————————————————————————————————————————————————————-
装饰器模式也叫做包装器模式,属于结构性设计模式一种,装饰器设计模式在实际的生活中也有很多体现,举例来说,某手机厂商发布了XXX标准版手机,为了吸引用户眼球,提升销量,还特别有纪念碑,青春版,至尊版等
我们可以这么设想,不管是哪个版本的手机,其基本的功能是不变的,电话、短信、拍照等这些基础的功能在各个版本都能找到
但是不同的版本,基于某个基础的版本,根据标价的不同添加了更多定制化或个性化的功能,相当于是说丰富了标准版的功能
- 装饰器模式作为现有类的一个包装,允许向现有的对象添加注入新的功能,同时还不改变原有的结构
- 给基础对象添加功能,一般有2种方式,继承或关联组合,将一个类的对象嵌入到另一个对象中,由另一个对象来决定是否调用嵌入对象的行为来增强功能,这个就是装饰器模式,相比继承更加灵活
定义一个手机接口
public interface Phone {
String desc();
int getPrice();
}
具体的被装饰类的实现,以手机为例,这里提供标准版手机,普通版
public class StandardPhone implements Phone{
private String desc = "标准版手机";
@Override
public String desc() {
return desc;
}
@Override
public int getPrice() {
return 1000;
}
}
public class MiddlePhone implements Phone{
private String desc = "普通版手机";
@Override
public String desc() {
return desc;
}
@Override
public int getPrice() {
return 2000;
}
}
手机装饰器(这个例子中没怎么使用)
public class PhoneDecorator implements Phone{
//装饰器在这里起到一个中间层作用,方便后面的扩展,具体的装饰实现延迟到子类中
private String desc = "这是装饰器";
@Override
public String desc() {
return desc;
}
@Override
public int getPrice() {
return 0;
}
}
添加了视频通话的装饰器类
public class VideoDecorator extends PhoneDecorator{
private String desc = "增加一个视频通话功能";
private int VideoPrice = 500;
private Phone phone;
public VideoDecorator(Phone phone){
this.phone = phone;
}
@Override
public String desc() {
return desc;
}
@Override
public int getPrice() {
return phone.getPrice()+VideoPrice;
}
}
增加了wifi充电功能的装饰器类
public class WifiChargeDecorator extends PhoneDecorator{
private String desc = "增加一个wifi充电功能";
private int WifiPrice = 200; //加价 200
private Phone phone;
public WifiChargeDecorator(Phone phone){
this.phone = phone;
}
@Override
public String desc() {
return desc;
}
@Override
public int getPrice() {
return phone.getPrice()+WifiPrice;
}
}
新增的手机的附加功能可以通过多个装饰类实现,方便客户端调用时自由选择,如果有更多的装饰类,可以按照上面的方式继续添加
下面来简单测试下,
1、获取一个普通版手机
public class PhoneTest {
public static void main(String[] args) {
//测试一下 获取一个标准版手机
Phone middlePhone = new MiddlePhone();
System.out.println(middlePhone.desc()+", "+middlePhone.getPrice()); //普通版手机, 2000
//增加视频通话功能
Phone videophone = new VideoDecorator(middlePhone);
System.out.println(videophone.desc()+", "+videophone.getPrice());//增加一个视频通话功能, 2500
//增加视频通话 和 Wifi充电功能
Phone wifi = new WifiChargeDecorator(videophone);
System.out.println(wifi.desc()+", "+wifi.getPrice());//增加视频通话 和 wifi充电功能, 2700
//只增加wifi充电功能
Phone onlywifi = new WifiChargeDecorator(middlePhone);
System.out.println(onlywifi.desc()+", "+onlywifi.getPrice()); //增加一个wifi充电功能, 2200
}
}
装饰器设计模式,优点:
装饰模式与继承关系的目的都是要扩展原有对象的功能,但是装饰器模式比继承增加了更多的灵活性
使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出更多的不同行为的组合,原有的代码无需改变,符合“开闭”原则