Save Calculation in Code or Database?

后端 未结 4 601
广开言路
广开言路 2021-01-14 09:27

I\'m setting up a system which will carry out various calculations on primitive data, and the provide an output based on the calculation.

My main question,

相关标签:
4条回答
  • 2021-01-14 09:55

    If I understand your question, you've got a database containing domain data, and a lot of calculations you execute on that data. Currently, you store those calculations as strings in the database, and execute those calculations using eval().

    There are some reasons for doing this I can imagine - it's easy to share the calculations among different calculation instances (so you can run multiple parallel calculations, or create new calculation clients (e.g. a web client, a command line client, a mobile app). It also makes managing the calculations fairly easy - they all live in a single place.

    However, there are some obvious downsides:

    • you can't easily test a new calculation. You have to insert it into the database and then see if anything breaks.
    • an attacker who can insert rows into you calculation table can get you to execute arbitrary code.
    • logically, the domain data and the calculation logic are not the same thing.

    So, you have a bunch of alternatives. In similar scenarios, I've often created a folder in my application structure with the calculations, and used file streams to load the calculation code dynamically. By using a source code control system with automated deployments, you can deploy this to any number of calculation instances.

    For more complex scenarios, I've built a little domain specific language. This is perfectly possible in PHP. In your case, this would allow you to remove a lot of the housekeeping logic ($primBVal != 0) and focus on the domain. It's non-trivial, but if you have hundreds of calculations, it might be worth it.

    0 讨论(0)
  • 2021-01-14 09:57

    If I were you, I won't put calculation php in database. Why? Let say, you decided to write your system in another language, you will need to parse that php codes in db, and convert it to your new language specifics(This is worst scenario). If I were you, I will convert this to a service, and divide it into pieces. You can put each call in to job queue, after job completion, you can save results in db. You can use this service for several type of clients.

    You can create functions for each calculations in order to change algorithm of calculations easily. Also, you can add new calculation as function.

    0 讨论(0)
  • 2021-01-14 10:04

    Eval is Evil

    First of all: don't use eval() unless there is a good reason. And there is never a good reason.

    in the worst case eval() makes your application vulnerable to injection attacks and also it's very slow. A bit of research reveals plenty of reasons why eval is a big no-no.

    Don't save your calculation code into the database

    If you do so and you would like to switch from PHP to another language you would still have PHP code in your database. It makes it really hard to migrate languages. You should always strive to make as many parts of your application as independent as possible.

    In this case you would tight-couple the language you use, to the database. That's a bad practice.

    Also the only possibilities to run your calculations from the database would be to eval them (which is bad, see above) or to disassemble the string with string operations or regex which causes unnecessary effort.

    It's all about Strategy

    In order to solve your problem you must execute code dependent of which calculation you need. That could be either done with switch-case-statements or if-statements. But that's also not a very elegant solution. Imagine you would need to execute other operations before calculating in the future, or extend functionality. You would need to update all your cases or if-statements.

    There is a nice design-pattern which is called Strategy Pattern. The strategy pattern solves problems when one use-case can be handled differently which is probably what you want.

    You want to calculate something (use-case) and there are different calculation types for it (different strategies)

    How it works

    To implement the Strategy pattern you basically need three things.

    • A class where you inject your strategies. It's basically a wrapper for your strategy tasks.
    • An interface which will be implemented by your strategies
    • Your strategies

    Your interface could look like this:

    <?php
    interface CalculatableInterface {
        
        public function calculate();
    
    }
    

    The interface will make sure that all your strategies provide a method to actually run the calculation. Nothing special.

    Next you may want to have a base class that takes your calculation operators as constructor arguments and stores them into properties.

    <?php
    abstract class Calculatable {
    
        protected $valueA;
        protected $valueB;
    
        public function __construct($valueA, $valueB)
        {
            $this->valueA = $valueA;
            $this->valueB = $valueB;
        }
    
    }
    

    Now it's getting serious. We are implementing our strategies.

    <?php
    class Division extends Calculatable implements CalculatableInterface {
    
        public function calculate()
        {
            return ($this->valueB != 0) ? $this->valueA / $this->valueB : 'NA';
        }
    
    }
    
    class Percentage extends Calculatable implements CalculatableInterface {
    
        public function calculate()
        {
            return ($this->valueB != 0) ? (100 / $this->valueB) * $this->valueA : 'NA';
        }
    
    }
    

    Of course you could clean this one up a bit, but what I want to point out here is the class declaration.

    We are extending our Calculatable class so that we can pass the arithmetic operations via constructor and we are implementing the CalculatableInterface which tells our class: "Hey! You must provide a calculate method, I don't care whether you want or not.

    We'll see later why this is an integral part of the pattern.

    So we have two concrete classes that contain the actual code for the actual arithmetic operation. If you would ever need to, you could change it easily as you see. To add more operations just add another class.

    Now we will create a class where our strategies can be injected. Later you will instantiate an object of this class and work with it.

    Here is how it looks like:

    <?php 
    class Calculator {
    
        protected $calculatable;
    
        public function __construct( CalculatableInterface $calculatable )
        {
            $this->calculatable = $calculatable;
        }
    
        public function calculate()
        {
            return $this->calculatable->calculate();
        }
    
    }
    

    The most important part here is the constructor. See how we type-hint our interface here. By doing that we make sure that only an object can be injected (Dependency Injection) whose class implements the interface. We do not need to demand a concrete class here. That's the crucial point here.

    Also there's a calculate method in there. It's just a wrapper for our strategy to execute it's calculate method.

    Wrapping it up

    So now we just need to create an object of our Calculator class and pass an object of one of our strategy classes (that contain the code for the arithmetic operations).

    <?php
    //The corresponding string is stored in your DB
    $calculatable = 'Division';
    
    $calc = new Calculator( new $calculatable(15, 100) );
    echo $calc->calculate();
    

    Try replacing the string stored in $calculatable to Percentage and you see that the operation for calculating the percentage will be executed.

    Conclusion

    The strategy pattern allowed you to create a clean interface for working with dynamic tasks that are only made concrete during runtime. Neither your database needs to know how we calculate things, nor your actual calculator does. The only thing we need to make sure is to code against an interface that provides a method to let us calculate things.

    0 讨论(0)
  • 2021-01-14 10:15

    I run a market research site where indeed many computes need to be done, performance indexes mainly. my experience and my actual work tells me that you should use php scripts instead of database strings, added on the fly. This because you have a workflow which can be better managed by moving and removing pieces of code . The database should work well as the workflow. manager or processs paramount .

    0 讨论(0)
提交回复
热议问题