-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Why final? #1
Comments
Hi @celtric! Sorry for the late answer, been quite busy recently. Let me explain our decicsion on the "final" keyword. We feel that the current class fits all our needs and adding the "final" keyword prevents somebody to accidentally (or not so accidentally...) extend the class to add more methods. One could argue that other organizations may haver other requirements and need more methods. Like you said you miss helpers like And about the Of course, you can prove me wrong with some other methods and we will gladly add it to the VO, this is just a proof of concept library that we're beginning to work with :) |
Thanks for the reply! Some quick thoughts before leaving for dinner:
Can't we trust people to follow LSP? Every library can be wrongly extended, but that doesn't mean their authors should mark them as final.
Speed is the main reason atm. We are still treating it as a VO, but a simple helper like class Money extends UlaboxMoney
{
public function format()
{
$formatter = new MoneyFormatter();
return $formatter->format($this);
}
} would come handy while we are creating a new design from scratch. We also wanted to extract the interface to not couple to your library, but it being final would've meant creating and wiring the methods, writing some unit test to check they are correctly linked, etc. If the library is kept final, we'll probably go this route once we find the time.
It was a (probably bad) example of a method we would probably add to reduce unnecessary code. We are using it store the value in MySQL, where we have a 2-decimal precision limit.
There are methods that may be useful to us but wouldn't be worth the effort adding them to the library. For example, we miss a static constructor where we don't have to instantiate a new Overall we are happy with it. We weren't aware that it's a proof of concept library (I don't think it's stated in the documentation), but we will continue using it in the future as we find it a useful and well-designed package. |
Thanks for your report! We're happy to discuss this :)
I'd suggest using composition if you really need this, it's a cleaner approach than inheritance IMHO. class MyCustomMoney
{
private $money
public function __construct()
{
$this->money = new Money();
}
public function toFloat()
{
return (float) $this->money->value();
}
public function __call($name, array $arguments)
{
return $this->money->{$name}(...$arguments);
}
}
IMHO this is a not the way to go, you're coupling the formatter to the VO. If we did that I would be forcing you to use my formatter whereas you may have very different formatting requirements. That's why you should have the formatter as a totally independent class.
You don't even need to instantiate that class, you don't need to preserve any state, use simple static calls.
Well, if you use Doctrine2 decimal values are restored as strings in PHP objects, not floats, (and this is great, no precision loss!) so no need for that! Decimal MySQL fields should never be casted to float when entering the PHP realm, they should be kept as strings an operate them using BCMath.
Contributions are always worth considering ;) About instantiating a new Currency every time you might be right but then you would need some kind of CurrencyFactory to instantiate new currencies if they weren't instantiated before or return the instance of the EUR currency. I guess it could work but it's simpler this way and the performance gain would be minimal. About the numeric check, we did it this way because when constructing new instances from the private methods (add, sub...) I am 100% sure that the type will be numeric, a check would be redundant. Hence we only assert the numeric type on public methods where we can't trust the input.
Of course! By proof of concept I meant that this is still an experiment but our plan is to use it "as is" quite heavily in the near future and we don't expect any major changes to it. |
We will do this once we have time. The problem is that we need static checking, and therefore we will have to manually create the methods and add some unit tests to check that everything is working as expected. This is more work than we were willing to put into an out-of-the-box money library, although now that we have settled for it we'll find some time to do it. Again, if it wasn't
Our need is very specific, as we were testing three different money libraries and speed was a priority. Once we have the design down we'll correct any flaws and clean the kitchen. I fully agree with you in that it should not be done this way, but by making it
Yes, it wasn't a good example (although we're using it to save data to MySQL, not restore it). We'll check the code to find any floats being passed to BCMath, as I was not aware that it could lead to problems.
I'd personally make the constructor public and restrictive (moving the For the record, this is what I would change: /**
* @param string $amount Amount, expressed as a string (eg '10.00')
* @param Currency $currency
*
* @throws InvalidArgumentException If amount is not a numeric string value
*/
- private function __construct($amount, Currency $currency)
+ public function __construct($amount, Currency $currency)
{
+ self::assertNumeric($amount);
+
$this->amount = $amount;
$this->currency = $currency;
}
/**
* Convenience factory method for a Money object
*
* <code>
* $fiveDollar = Money::USD(500);
* </code>
*
* @param string $method
* @param array $arguments
*
* @return Money
*/
public static function __callStatic($method, $arguments)
{
- self::assertNumeric($arguments[0]);
return new self((string) $arguments[0], Currency::fromCode($method));
}
/**
* Creates a Money object from its amount and currency
*
* @param numeric $amount
- * @param Currency $currency
+ * @param Currency|string $currency
*
* @return Money
*/
- public static function fromAmount($amount, Currency $currency)
+ public static function fromAmount($amount, $currency)
{
- self::assertNumeric($amount);
+ if (!$currency instanceof Currency) {
+ $currency = new Currency($currency);
+ }
+
return new self((string) $amount, $currency);
} |
I've ended up creating a fork to solve other issues I had (clashing namespaces, numeric typehinting, etc.). I'm closing the issue then, thanks! |
@celtric keep us posted with your fork, maybe we can agree on some features and merge both forks again in the future! I'm sure that when we start to fully leverage the library we're going to find more issues and update the librearary a bit more. Thanks for your time :) |
Sure! If you consider these issues in the future I'll be happy to delete the fork and continue using your library, as I'd prefer to use you as a source than to maintain a fork myself. Checking my commits, these are the things I've changed:
I'll keep you updated if some of these decisions come back to bite me and have to rollback! |
Does it make sense to mark it as such, knowing that it's a public library and users are expected to tinker with it? It's currently preventing us from adding everyday helpers like
format()
ortoFloat()
, and we don't really see the benefit.The text was updated successfully, but these errors were encountered: