一些可重用代码的架构

发布于 2024-07-26 01:42:48 字数 1116 浏览 2 评论 0原文

我正在编写一些小型、简单的应用程序,它们共享一个共同的结构,并且需要以相同的方式做一些相同的事情(例如日志记录、数据库连接设置、环境设置),并且我正在寻找一些构建这些应用程序的建议可重复使用的组件。 该代码是用强静态类型语言编写的(例如Java 或C#,我必须用这两种语言来解决这个问题)。 目前我的想法是:

abstract class EmptyApp //this is the reusable bit
{
   //various useful fields: loggers, db connections

   abstract function body()
   function run()
   {
        //do setup
        this.body()
        //do cleanup
   }
}

class theApp extends EmptyApp //this is a given app
{
   function body()
   {
        //do stuff using some fields from EmptyApp
   }

   function main()
   {
        theApp app = new theApp()
        app.run()
   }
 }

有更好的方法吗? 也许如下? 我无法权衡权衡...

abstract class EmptyApp
{
     //various fields
}

class ReusableBits
{
    static function doSetup(EmptyApp theApp)

    static function doCleanup(EmptyApp theApp)
}

class theApp extends EmptyApp
{
    function main()
    {
         ReusableBits.doSetup(this);
         //do stuff using some fields from EmptyApp
         ReusableBits.doCleanup(this);
    }
}

一个明显的权衡是,使用选项 2,“框架”无法将应用程序包装在 try-catch 块中...

I am writing a number of small, simple applications which share a common structure and need to do some of the same things in the same ways (e.g. logging, database connection setup, environment setup) and I'm looking for some advice in structuring the reusable components. The code is written in a strongly and statically typed language (e.g. Java or C#, I've had to solve this problem in both). At the moment I've got this:

abstract class EmptyApp //this is the reusable bit
{
   //various useful fields: loggers, db connections

   abstract function body()
   function run()
   {
        //do setup
        this.body()
        //do cleanup
   }
}

class theApp extends EmptyApp //this is a given app
{
   function body()
   {
        //do stuff using some fields from EmptyApp
   }

   function main()
   {
        theApp app = new theApp()
        app.run()
   }
 }

Is there a better way? Perhaps as follows? I'm having trouble weighing the trade-offs...

abstract class EmptyApp
{
     //various fields
}

class ReusableBits
{
    static function doSetup(EmptyApp theApp)

    static function doCleanup(EmptyApp theApp)
}

class theApp extends EmptyApp
{
    function main()
    {
         ReusableBits.doSetup(this);
         //do stuff using some fields from EmptyApp
         ReusableBits.doCleanup(this);
    }
}

One obvious tradeoff is that with option 2, the 'framework' can't wrap the app in a try-catch block...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

仙女山的月亮 2024-08-02 01:42:48

我一直赞成通过组合(您的第二个选择)而不是继承(您的第一个选择)来重用。

仅当类之间存在关系时才应使用继承,而不是为了代码重用。

因此,对于您的示例,我将有多个 ReusableBits 类,每个类执行每个应用程序在需要时使用的一件事。

这允许每个应用程序重复使用与该特定应用程序相关的框架部分,而不必被迫采用所有内容,从而允许各个应用程序更加自由。 如果您将来的某些应用程序不完全适合您今天所考虑的结构,那么通过继承进行重用有时会变得非常具有限制性。

如果您将框架分解为单独的实用程序,您还会发现单元测试和测试驱动开发变得更加容易。

I've always favored re-use through composition (your second option) rather than inheritance (your first option).

Inheritance should only be used when there is a relationship between the classes rather than for code reuse.

So for your example I would have multiple ReusableBits classes each doing 1 thing that each application a make use of as/when required.

This allows each application to re-use the parts of your framework that are relevant for that specific application without being forced to take everything, Allowing the individual applications more freedom. Re-use through inheritance can sometimes become very restrictive if you have some applications in the future that don't exactly fit into the structure you have in mind today.

You will also find unit testing and test driven development much easier if you break your framework up into separate utilities.

梦途 2024-08-02 01:42:48

为什么不让框架调用您的可定制代码呢? 因此,您的客户端创建一些对象,并将其注入到框架中。 框架初始化,调用setup()等,然后调用客户端的代码。 完成后(或者甚至在引发异常后),框架会调用 cleanup() 并退出。

因此,您的客户端只需实现一个接口(例如(在 Java 中))

public interface ClientCode {

    void runClientStuff(); // for the sake of argument
}

,框架代码就配置了该接口的实现,并在需要时调用 runClientStuff()

因此,您不必从应用程序框架派生,而只需提供符合特定约定的类。 您可以在运行时配置应用程序设置(例如客户端将向应用程序提供什么类),因为您不是从应用程序派生的,因此您的依赖关系不是静态的。

上面的接口可以扩展为具有多个方法,并且应用程序可以在生命周期的不同阶段调用所需的方法(例如,提供特定于客户端的设置/清理),但这是功能蔓延的一个示例:-)

Why not make the framework call onto your customisable code ? So your client creates some object, and injects it into the framework. The framework initialises, calls setup() etc., and then calls your client's code. Upon completion (or even after a thrown exception), the framework then calls cleanup() and exits.

So your client would simply implement an interface such as (in Java)

public interface ClientCode {

    void runClientStuff(); // for the sake of argument
}

and the framework code is configured with an implementation of this, and calls runClientStuff() whenever required.

So you don't derive from the application framework, but simply provide a class conforming to a particular contract. You can configure the application setup at runtime (e.g. what class the client will provide to the app) since you're not deriving from the app and so your dependency isn't static.

The above interface can be extended to have multiple methods, and the application can call the required methods at different stages in the lifecycle (e.g. to provide client-specific setup/cleanup) but that's an example of feature creep :-)

所有深爱都是秘密 2024-08-02 01:42:48

请记住,只有当所有继承的对象由于其相似性而重用代码时,继承才是一个好的选择。 或者如果您希望呼叫者能够在同一个裂变中与他们进行交互。
如果我刚才提到的内容适用于您,那么根据我的经验,在您的基/抽象类中拥有通用逻辑总是更好。

这就是我用 C# 重写示例应用程序的方式。

abstract class BaseClass
{
    string field1 = "Hello World";
    string field2 = "Goodbye World";

    public void Start()
    {
        Console.WriteLine("Starting.");
        Setup();
        CustomWork();
        Cleanup();
    }

    public virtual void Setup()
    {Console.WriteLine("Doing Base Setup.");}

    public virtual void Cleanup()
    {Console.WriteLine("Doing Base Cleanup.");}

    public abstract void CustomWork();
}

class MyClass : BaseClass
{
    public override void CustomWork()
    {Console.WriteLine("Doing Custome work.");}

    public override void Cleanup()
    {
        Console.WriteLine("Doing Custom Cleanup");
        //You can skip the next line if you want to replace the
        //cleanup code rather than extending it
        base.Cleanup();
    }

}

void Main()
{
    MyClass worker = new MyClass();
    worker.Start();
}

Remember, inheritance is only a good choice if all the object that are inheriting reuse the code duo to their similarities. or if you want callers to be able to interact with them in the same fission.
if what i just mentioned applies to you then based on my experience its always better to have the common logic in your base/abstract class.

this is how i would re-write your sample app in C#.

abstract class BaseClass
{
    string field1 = "Hello World";
    string field2 = "Goodbye World";

    public void Start()
    {
        Console.WriteLine("Starting.");
        Setup();
        CustomWork();
        Cleanup();
    }

    public virtual void Setup()
    {Console.WriteLine("Doing Base Setup.");}

    public virtual void Cleanup()
    {Console.WriteLine("Doing Base Cleanup.");}

    public abstract void CustomWork();
}

class MyClass : BaseClass
{
    public override void CustomWork()
    {Console.WriteLine("Doing Custome work.");}

    public override void Cleanup()
    {
        Console.WriteLine("Doing Custom Cleanup");
        //You can skip the next line if you want to replace the
        //cleanup code rather than extending it
        base.Cleanup();
    }

}

void Main()
{
    MyClass worker = new MyClass();
    worker.Start();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文