古怪的泛型循环 CRG

他能产生使用子类作为参数 和 返回类型 的父类

  1. class BasicHolder<T>{
  2. private T item;
  3. public void setItem(T item) {
  4. this.item = item;
  5. }
  6. public T getItem() {
  7. return item;
  8. }
  9. void f(){
  10. System.out.println(item.getClass().getSimpleName());
  11. }
  12. }
  13. class SubType extends BasicHolder<SubType>{}
  14. public class Holder {
  15. public static void main(String[] args) {
  16. SubType s1 = new SubType(), s2 = new SubType();
  17. s1.setItem(s2);
  18. SubType s3 = s1.getItem();
  19. s1.f();
  20. }
  21. }
  22. SubType

基类用子类替代其参数,因此在SubType中,传递给setItem()和getItem()的返回类型都是确切的SubType。

混型

混型最基本的概念是混合多个类的能力,以产生一个可以表示混型中所有类型的类

混型与接口相结合

例如 现在需要把获取当前时间戳、获取计数器和存取某个值的三个类合成一个混型。

  1. interface TimeStamped {
  2. Date getStamp();
  3. }
  4. class TimeStampedImp implements TimeStamped {
  5. private final Date timeStamp;
  6. public TimeStampedImp() {
  7. timeStamp = new Date();
  8. }
  9. @Override
  10. public Date getStamp() {
  11. return timeStamp;
  12. }
  13. }
  14. interface SerialNumbered {
  15. long getSerialNumber();
  16. }
  17. class SerialNumberedImp implements SerialNumbered {
  18. private static long counter = 1;
  19. private final long serialNumber = counter++;
  20. @Override
  21. public long getSerialNumber() {
  22. return serialNumber;
  23. }
  24. }
  25. interface Basic {
  26. public void set(String val);
  27. public String get();
  28. }
  29. class BasicImp implements Basic {
  30. private String value;
  31. @Override
  32. public void set(String val) {
  33. value = val;
  34. }
  35. @Override
  36. public String get() {
  37. return value;
  38. }
  39. }
  40. //自己创建的混型
  41. interface Game{
  42. String lol();
  43. }
  44. class GameImp implements Game{
  45. private String str;
  46. public GameImp() {
  47. str = "来把LOL";
  48. }
  49. @Override
  50. public String lol(){
  51. return str;
  52. }
  53. }
  54. //实现获取时间 和 序列号
  55. class Mixin extends BasicImp implements TimeStamped, SerialNumbered ,Game{
  56. private TimeStamped timeStamp = new TimeStampedImp();
  57. private SerialNumbered serialNumber = new SerialNumberedImp();
  58. private Game str = new GameImp();
  59. @Override //每加入一个混型就要重写方法,代码量增大
  60. public Date getStamp() {
  61. return timeStamp.getStamp();
  62. }
  63. @Override
  64. public long getSerialNumber() {
  65. return serialNumber.getSerialNumber();
  66. }
  67. @Override
  68. public String lol() {
  69. return str.lol();
  70. }
  71. }
  72. public class Mixins {
  73. public static void main(String[] args) {
  74. Mixin mixin1 = new Mixin(), mixin2 = new Mixin();
  75. mixin1.set("测试1");
  76. System.out.println(mixin1.get() + " " +
  77. mixin1.getStamp() + " " + mixin1.getSerialNumber()+" "+mixin1.lol());
  78. mixin2.set("测试2");
  79. System.out.println(mixin2.get() + " " +
  80. mixin2.getStamp() + " " + mixin2.getSerialNumber());
  81. }
  82. }
  83. 测试1 Mon May 16 08:41:31 CST 2022 1 来把LOL
  84. 测试2 Mon May 16 08:41:31 CST 2022 2

Mixin 类基本上在使用代理,因此每个混入类型都要求在Mixin 中有一个相应的域,还必须在Mixin 中编写所有必须的方法。 这样下来当 使用更复杂的混型时,代码数量会急速增加。

通过动态代理来实现混型

由于动态代理的限制,每个被混入的类都要求为接口。

  1. public class TwoTuple<A, B> {
  2. public TwoTuple(A first, B second) {
  3. this.first = first;
  4. this.second = second;
  5. }
  6. private A first;
  7. private B second;
  8. public A getFirst() {
  9. return first;
  10. }
  11. public B getSecond() {
  12. return second;
  13. }
  14. }
  1. class MixinProxy implements InvocationHandler {
  2. // 在 MixinProxy 代理类的构造方法中,将需要代理的方法作为 key ,
  3. // 实现代理方法的实现类作为 value 存入 delegatesByMethod 中。
  4. private Map<String, Object> delegatesByMethod;
  5. //TwoTuple 存储两个类型参数,第一个为被代理接口的实现类,第二个为被代理接口。
  6. public MixinProxy(TwoTuple<Object, Class<?>> ... pairs) {
  7. delegatesByMethod = new HashMap<String, Object>();
  8. for (TwoTuple<Object, Class<?>> pair : pairs) {
  9. //getMethods(): 返回一个包含Method对象的数组,
  10. // 该数组反映由这个class对象表示的类或接口的所有公共方法
  11. for (Method method : pair.second.getMethods()) {
  12. //获取要代理接口中所有的Method对象
  13. //method.getName(): 返回由这个method对象表示的方法的名称,作为一个String。
  14. String methodName = method.getName();
  15. //System.out.print(methodName+" ");
  16. //判断是否包含将要代理的接口中的方法 不包含的话 将Method与实现类对应
  17. //即动态绑定 Map中保存的是每一个具体实现类与将要代理的方法一一对应
  18. //将需要代理的方法作为 key ,实现代理方法的实现类作为 value 存入 delegatesByMethod 中。
  19. if (!delegatesByMethod.containsKey(methodName))
  20. delegatesByMethod.put(methodName, pair.first);
  21. }
  22. }
  23. }
  24. //当指定的接口类的方法被调用时 真正的调用发生在此处 根据调用的方法 去Map中查找对于的具体实现类 然后通过反射调用
  25. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  26. String methodName = method.getName();
  27. Object delegate = delegatesByMethod.get(methodName);
  28. return method.invoke(delegate, args);
  29. }
  30. @SuppressWarnings("unchecked")
  31. //在此处处理多个接口与多个实现类的关系 产生真正的代理对象
  32. //通过 newInstance() 方法获取实例时,传入第一个接口实现类的类加载器、需要被代理的接口、实现 InvocationHandler 接口的类
  33. // (任意一个接口实现类的加载器都行,传入第一个是防止因下标越界引起异常)
  34. //返回代理对象
  35. public static Object newInstance(TwoTuple ... pairs) {
  36. Class[] interfaces = new Class[pairs.length];
  37. for (int i = 0; i < pairs.length; i++) {
  38. interfaces[i] = (Class) pairs[i].second;
  39. }
  40. //传入第一个接口实现类的类加载器
  41. ClassLoader cl = pairs[0].first.getClass().getClassLoader();
  42. return Proxy.newProxyInstance(cl, interfaces, new MixinProxy(pairs));
  43. }
  44. }
  45. public class DynamicProxyMixin {
  46. public static void main(String[] args) {
  47. //因代理对象同时代理了多组接口,
  48. // 故可以转换为 Basic 、TimeStamped 、SerialNumbered 任意类型进行方法调用。
  49. Object mixin = MixinProxy.newInstance(
  50. tuple(new BasicImp(), Basic.class),
  51. tuple(new TimeStampedImp(), TimeStamped.class),
  52. tuple(new SerialNumberedImp(), SerialNumbered.class),
  53. tuple(new GameImp(), Game.class)); //现在加入一个游戏类
  54. Basic b = (Basic) mixin;
  55. TimeStamped t = (TimeStamped) mixin;
  56. SerialNumbered s = (SerialNumbered) mixin;
  57. b.set("Hello");
  58. System.out.println(b.get());
  59. System.out.println(t.getStamp());
  60. System.out.println(s.getSerialNumber());
  61. System.out.println(g.lol()); //调用游戏类方法
  62. }
  63. }
  64. 输出:
  65. Hello
  66. Mon May 16 09:04:06 CST 2022
  67. 1
  68. 来把LOL

倘若我们需要添加新的类型至混型之中,只需要拓展对应的接口。相比直接通过接口实现混型,此方式的代码量更少,也更为灵活

装饰器模式

装饰器是通过使用 组合 和 形式化结构 (可装饰物/装饰器层次结构)来实现的,而混型是基于继承的。

因此可以将基于参数化类型的混型当作是一种泛型装饰器机制 。 这种机制不需要装饰器设计模式的继承结构
—————————————————————————————————————————————————————————-
装饰器模式也叫做包装器模式,属于结构性设计模式一种,装饰器设计模式在实际的生活中也有很多体现,举例来说,某手机厂商发布了XXX标准版手机,为了吸引用户眼球,提升销量,还特别有纪念碑,青春版,至尊版等
我们可以这么设想,不管是哪个版本的手机,其基本的功能是不变的,电话、短信、拍照等这些基础的功能在各个版本都能找到
但是不同的版本,基于某个基础的版本,根据标价的不同添加了更多定制化或个性化的功能,相当于是说丰富了标准版的功能

  • 装饰器模式作为现有类的一个包装,允许向现有的对象添加注入新的功能,同时还不改变原有的结构
  • 给基础对象添加功能,一般有2种方式,继承或关联组合,将一个类的对象嵌入到另一个对象中,由另一个对象来决定是否调用嵌入对象的行为来增强功能,这个就是装饰器模式,相比继承更加灵活

定义一个手机接口

  1. public interface Phone {
  2. String desc();
  3. int getPrice();
  4. }

具体的被装饰类的实现,以手机为例,这里提供标准版手机,普通版

  1. public class StandardPhone implements Phone{
  2. private String desc = "标准版手机";
  3. @Override
  4. public String desc() {
  5. return desc;
  6. }
  7. @Override
  8. public int getPrice() {
  9. return 1000;
  10. }
  11. }
  1. public class MiddlePhone implements Phone{
  2. private String desc = "普通版手机";
  3. @Override
  4. public String desc() {
  5. return desc;
  6. }
  7. @Override
  8. public int getPrice() {
  9. return 2000;
  10. }
  11. }

手机装饰器(这个例子中没怎么使用)

  1. public class PhoneDecorator implements Phone{
  2. //装饰器在这里起到一个中间层作用,方便后面的扩展,具体的装饰实现延迟到子类中
  3. private String desc = "这是装饰器";
  4. @Override
  5. public String desc() {
  6. return desc;
  7. }
  8. @Override
  9. public int getPrice() {
  10. return 0;
  11. }
  12. }

添加了视频通话的装饰器类

  1. public class VideoDecorator extends PhoneDecorator{
  2. private String desc = "增加一个视频通话功能";
  3. private int VideoPrice = 500;
  4. private Phone phone;
  5. public VideoDecorator(Phone phone){
  6. this.phone = phone;
  7. }
  8. @Override
  9. public String desc() {
  10. return desc;
  11. }
  12. @Override
  13. public int getPrice() {
  14. return phone.getPrice()+VideoPrice;
  15. }
  16. }

增加了wifi充电功能的装饰器类

  1. public class WifiChargeDecorator extends PhoneDecorator{
  2. private String desc = "增加一个wifi充电功能";
  3. private int WifiPrice = 200; //加价 200
  4. private Phone phone;
  5. public WifiChargeDecorator(Phone phone){
  6. this.phone = phone;
  7. }
  8. @Override
  9. public String desc() {
  10. return desc;
  11. }
  12. @Override
  13. public int getPrice() {
  14. return phone.getPrice()+WifiPrice;
  15. }
  16. }

新增的手机的附加功能可以通过多个装饰类实现,方便客户端调用时自由选择,如果有更多的装饰类,可以按照上面的方式继续添加
下面来简单测试下,
1、获取一个普通版手机

  1. public class PhoneTest {
  2. public static void main(String[] args) {
  3. //测试一下 获取一个标准版手机
  4. Phone middlePhone = new MiddlePhone();
  5. System.out.println(middlePhone.desc()+", "+middlePhone.getPrice()); //普通版手机, 2000
  6. //增加视频通话功能
  7. Phone videophone = new VideoDecorator(middlePhone);
  8. System.out.println(videophone.desc()+", "+videophone.getPrice());//增加一个视频通话功能, 2500
  9. //增加视频通话 和 Wifi充电功能
  10. Phone wifi = new WifiChargeDecorator(videophone);
  11. System.out.println(wifi.desc()+", "+wifi.getPrice());//增加视频通话 和 wifi充电功能, 2700
  12. //只增加wifi充电功能
  13. Phone onlywifi = new WifiChargeDecorator(middlePhone);
  14. System.out.println(onlywifi.desc()+", "+onlywifi.getPrice()); //增加一个wifi充电功能, 2200
  15. }
  16. }

装饰器设计模式,优点:

装饰模式与继承关系的目的都是要扩展原有对象的功能,但是装饰器模式比继承增加了更多的灵活性
使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出更多的不同行为的组合,原有的代码无需改变,符合“开闭”原则

缺点:

装饰器模式添加了许多子类,过多使用会使程序变得很复杂
增加了系统的复杂程度,加大了使用者的学习成本和理解难度