草庐IT

java - Java 中装饰器模式的替代方案?

coder 2024-03-13 原文

假设您有以下统计相关类的层次结构,其结构类似于 Template method pattern :

interface S {
   // Method definitions up-to and including the S3 class
}

class S0 implements S {
   // Code that counts samples
}

class S1 extends S0 {
   // Code that calls the superclass methods and also computes the mean
}

class S2 extends S1 {
   // Code that calls the superclass methods and also computes the variance
}

class S3 extends S2 {
   // Code that calls the superclass methods and also computes the skewness
}

现在假设我们想将这些类中的每一个扩展到例如检查指标的收敛性。出于我的目的,我不需要在运行时进行此扩展。我可以想到以下替代方案:
  • 创建子类 S0C , S1C , S2CS3C来自 S0 , S1 , S2S3分别带有检查收敛的代码副本:
  • 优点:
  • 概念上很直接
  • 结果对象仍然属于父类(super class)
  • 子类源代码仅包含额外的收敛检查代码
  • 缺点:
  • 大量的代码重复 - future 会导致更改同步开销
  • 主要缺点:
  • 如果我想要另一组类,例如预处理 sample ?我们正在谈论相同代码的指数复制!
  • 使用 Decorator pattern :
  • 优点:
  • 没有代码重复!
  • 缺点:
  • 对象不再属于原始类(很容易解决)
  • 由于使用虚拟方法调用而不是特殊方法调用,Java 中的性能受到了非常轻微的影响(它存在!我测量过!)。这不是很重要,但仍然很明显。
  • 主要缺点:
  • 大约有无数的委托(delegate)方法必须与包装的对象接口(interface)保持同步。使用接口(interface)确实确保不会遗漏任何方法,但它仍然难以维护,即使使用自动生成委托(delegate)方法的 IDE。
  • 为了有一个正确实现的装饰器模式,所有的装饰器和包装类都需要实现完全相同的接口(interface)。这基本上意味着我必须添加例如收敛检查方法到S接口(interface),这完全破坏了任何模块化的感觉。
    取消此要求的唯一方法是在我的代码中禁止嵌套装饰器。

  • 如果 Java 支持多重继承,我可能能够通过继承统计和基本收敛检查(或其他)类来处理这个问题。唉,Java 不支持多重继承(不,接口(interface)不算数!)。

    有没有更好的方法来处理 Java 中的这个问题?也许是不同的设计模式?更具技术性的解决方案?某种特殊的仪式舞蹈?

    PS:如果我误解了什么,请随时(温和地)指出...

    编辑:

    看来我需要澄清一下我的目标:
  • 我不需要运行时对象组合。我想要的是扩展 S* 的功能具有新方法的类。如果我可以根据需要创建子类而无需重复代码,我可能会这样做。如果我能在使用地点(不太可能)做到这一点,那就更好了。
  • 我宁愿不一遍又一遍地编写相同的代码。注意:委托(delegate)方法和构造函数很好,我想,实现算法的方法不是。
  • 我想保持我的接口(interface)模块化。这是我的装饰器模式的主要问题 - 除非放置非常具体的嵌套约束,否则您最终会得到所有接口(interface)的 super 接口(interface)...

  • 编辑2:

    要解决一些评论:
  • S*类是使用模板方法构建的:
    class S0 {
       int addSample(double x) {
          ...;
       }
    
      double getMean() {
          return Double.NaN;
      }
    }
    
    class S1 extends S0 {
    
    
       int addSample(double x) {
          super.addSample(x);
          ...;
       }
    
       double getMean() {
          return ...;
       }
    }
    
  • 我的 S*C第一个解决方案的扩展类将是这样的:
    interface S {
        int addSample(double x);
        double getMean();
    }    
    
    class S0C extends S0 implements S {
       int addSample(double x) {
          super.addSample(x);
          ...;
       }
    
       boolean hasConverged() {
          return ...;
       }
    }
    
    class S1C extends S1 {
       int addSample(double x) {
          super.addSample(x);
          ...;
       }
    
       boolean hasConverged() {
          return ...;
       }
    }
    

    注意 hasConverged() 的重复方法。
  • 收敛检查装饰器将是这样的:
    class CC<T extends S> implements S {
       T o = ...;
    
       int addSample(double x) {
          o.addSample(x);
          ...;
       }
    
       double getMean() {
          return o.getMean();
       }    
    
       boolean hasConverged() {
          return ...;
       }
    }
    

    问题:如果我想结合除收敛检查之外的另一个分隔符行为,我需要一个单独的装饰器,例如NB - 并且为了能够访问例如hasConverged()方法,新的装饰器需要:
  • 实现与 CC 相同的接口(interface)
  • 使用与CC相同的接口(interface)对于其包装的对象类型...
  • ...这迫使我将该接口(interface)用于 S*方法,如果我想能够使用 NBS*不使用的对象 CC
  • 我选择装饰者模式只是因为缺乏更好的选择。这只是我迄今为止找到的最干净的解决方案。
  • 扩展 S* 时类,我仍然需要原件完好无损。放例如通用父类(super class)中的收敛功能意味着相关行为(及其性能影响)现在将存在于所有子类中,这绝对不是我想要的。
  • 最佳答案

    根据您最近的编辑。

    正如您可能已经意识到的那样,装饰器不适合于此。这是因为它解决的是单个功能的扩充,而不是整个类树的扩充。

    实现这一目标的一种可能方式是使用策略。策略以算法为重点;它允许您解耦行为代码(对不起,如果这里和那里有一点 C#)

    样本类

    public class S {
       private List<Integer> Samples = new List<Integer>(); 
    
       public void addSample(int x){
          Samples.Add(new Integer(x));
       }
    
       public void Process(IOp[] operations){
          for (Op operation : operations){
              Process(operation);
          }
       }
       public void Process(ICollection<IOp> operations){
          for (Op operation : operations){
              Process(operation);
          }
       }
       public void Process(IOp operation){
          operation.Compute(this.Samples);
       }
    }
    

    操作
    public interface IOp { 
       // Interface is optional. Just for flexibility. 
       public void Compute(List<Integer> data);
    }
    public class Op<T> implements IOp{ 
       // Generics is also optional. I use this to standardise data type of Result, so that it can be polymorphically accessed.
       // You can also put in some checks to make sure Result is initialised before it is accessed.
       public T Result;
    
       public void Compute(List<Integer> data);
    }
    class ComputeMeanOperation extends Op<double>{
       public void Compute(List<Integer> data){
           /* sum and divide to get mean */
           this.Result = /* ... */
       }
    }
    class CheckConvergenceOperation extends Op<boolean>{
       public void Compute(List<Integer> data){
           /* check convergence */
           this.Result = /* ... */
       }
    }
    

    用法
    public static void main(String args[]) {
        S s = new S();
        s.addSample(1);
        /* ... */
    
        ComputeMeanOperation op1 = new ComputeMeanOperation();
        CheckConvergenceOperation op2 = new CheckConvergenceOperation ();        
    
        // Anonymous Operation
        Op<Integer> op3 = new Op<Integer>(){
           public void Compute(List<Integer> samples){
               this.Result = samples[0]; // Gets first value of samples
           }
        }
    
        s.Process(op1); // Or use overloaded methods
        s.Process(op2);
        s.Process(op3);
    
        System.out.println("Op1 result: " + op1.Result); 
        System.out.println("Op2 result: " + op2.Result);
        System.out.println("Op3 result: " + op3.Result);
    }
    

    优点:
  • 您可以根据需要任意添加和删除操作。
  • 样本类没有额外的变化。
  • 样本类是内聚数据结构。
  • 模块化:每个操作都是自包含的。接口(interface)只公开需要的东西。与每个操作交互的通用过程。
  • 如果您出于某种原因需要重复执行此操作,则可以将所有操作存储在一个数组中,并在循环中重复使用。比调用 4-5 个方法并存储结果要干净得多。

  • 缺点/限制:
  • 如果您的操作需要大量数据,那么您必须将这些数据公开给您的操作,从而增加耦合(如果需要,我可以编辑帖子)。在我的示例中,我只是传递了一个示例列表。如果需要,您可能必须传入整个数据结构。
  • 如果您有任何依赖于另一个操作结果的操作,这将无法开箱即用。 (这可以使用 Composite 来完成 - 一个由多个 Op 组成的大型 Op,其结果传递给下一个。)

  • 希望这符合您的要求:)

    关于java - Java 中装饰器模式的替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7707363/

    有关java - Java 中装饰器模式的替代方案?的更多相关文章

    1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    3. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

      在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

    4. ruby - 如何在续集中重新加载表模式? - 2

      鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

    5. java - 等价于 Java 中的 Ruby Hash - 2

      我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

    6. ruby-on-rails - 更好的替代方法 try( :output). try( :data). try( :name)? - 2

      “输出”是一个序列化的OpenStruct。定义标题try(:output).try(:data).try(:title)结束什么会更好?:) 最佳答案 或者只是这样:deftitleoutput.data.titlerescuenilend 关于ruby-on-rails-更好的替代方法try(:output).try(:data).try(:name)?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c

    7. java - 从 JRuby 调用 Java 类的问题 - 2

      我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

    8. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

      给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

    9. java - 我的模型类或其他类中应该有逻辑吗 - 2

      我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

    10. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

      什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

    随机推荐