Self-review
Randomize missing
Since I use Random to generate pseudo-random color, it is, I lightly remember, a must to call Randomize once program or unit in this case is being created; more information.
// ...
initialization
Randomize;
end.
Get... Red, Green, Blue, Cyan, Magenta, Yellow
These seem to operate as expected.
constructors
The biggest error I already see I have made is the constructor thing. The problem is I did not write one for direct TColor assignment. It could be re-written for instance like this in the interface:
type
TBasicColor = class
// ...
public
// default direct TColor assignment constructor
constructor Create(const AColor: TColor); overload;
// reintroduce is hiding TObject constructor
constructor Create; reintroduce; overload;
// create using RGB values
constructor CreateRGB(const ARed, AGreen, ABlue: Byte);
// create using CMY values
constructor CreateCMY(const ACyan, AMagenta, AYellow: Byte);
// create pseudo-random color constructor
constructor CreateRandom;
// ...
Plus, like this in the implementation:
constructor TBasicColor.Create(const AColor: TColor);
begin
// in here it is just plain assignment
inherited Create;
FColorRef := AColor;
end;
constructor TBasicColor.Create;
begin
// in case anyone just calls Create() we assign white color
Create($FFFFFF);
end;
constructor TBasicColor.CreateRGB(const ARed, AGreen, ABlue: Byte);
begin
Create(ARed or (AGreen shl 8) or (ABlue shl 16));
end;
constructor TBasicColor.CreateCMY(const ACyan, AMagenta, AYellow: Byte);
begin
CreateRGB(255 - ACyan, 255 - AMagenta, 255 - AYellow);
end;
constructor TBasicColor.CreateRandom;
begin
Create(Random($FFFFFF + 1));
end;
As you can see, all, in the end, are calling the default constructor, which I see as much better implementation.
overload keyword
Note on overload keyword, I originally did not need it in Lazarus, but Delphi requires it.
Comments
By the way, I should really use more comments, they will prove useful one day, once I read it after years.
Why read-only ColorRef?
On second thought, I see no reason for the ColorRef not being able to change at runtime, I find it hard to see what reason I had before, but no matter, it should stay a private member with properties to safely read and write, also the typecast might be wrong, cannot confirm or disprove at this point, best to typecast when necessary in-place.
For example with private method Assign:
procedure TBasicColor.Assign(const ColorRef: TColor);
begin
if (ColorRef < 0) or (ColorRef > $FFFFFF)
then raise ERangeError.Create('ERangeError in TBasicColor class.' + sLineBreak +
'It supports only subset of TColor range.' + sLineBreak +
'Valid range is <0; $FFFFFF>.')
else FColorRef := ColorRef;
end;
which can in turn be used in the SetColorRef setter:
procedure TBasicColor.SetColorRef(const ColorRef: TColor);
begin
Assign(ColorRef);
end;
ARed change to Red, etc.
I believe it's a habit or style point, but anyway.
I removed, and am no longer a fan of an A prefixing, changed to this:
constructor TBasicColor.CreateRGB(const Red, Green, Blue: Byte);
constructor TBasicColor.CreateCMY(const Cyan, Magenta, Yellow: Byte);
Modified code
After a few other adjustments, I will name only the use of setters in all color components, this unit could be re-written finally to this state:
unit basic_color;
interface
uses
Graphics, SysUtils;
type
TBasicColor = class
strict private
FColorRef: TColor;
private
// TColor assignment with range check <0; $FFFFFF>
procedure Assign(const ColorRef: TColor);
// independent function needed (Delphi/Lazarus; Windows/Linux)
function RGBToColor(const Red, Green, Blue: Byte): TColor;
protected
function GetColorRef: TColor;
procedure SetColorRef(const ColorRef: TColor);
function GetRed: Byte;
procedure SetRed(const NewRed: Byte);
function GetGreen: Byte;
procedure SetGreen(const NewGreen: Byte);
function GetBlue: Byte;
procedure SetBlue(const NewBlue: Byte);
function GetCyan: Byte;
procedure SetCyan(const NewCyan: Byte);
function GetMagenta: Byte;
procedure SetMagenta(const NewMagenta: Byte);
function GetYellow: Byte;
procedure SetYellow(const NewYellow: Byte);
public
// default direct TColor assignment
constructor Create(const ColorRef: TColor); overload;
// reintroduce is hiding TObject default constructor
constructor Create; reintroduce; overload;
// create color using RGB values
constructor CreateRGB(const Red, Green, Blue: Byte);
// create color using CMY values
constructor CreateCMY(const Cyan, Magenta, Yellow: Byte);
// create pseudo-random color
constructor CreateRandom;
property ColorRef: TColor read GetColorRef write SetColorRef;
property Red: Byte read GetRed write SetRed;
property Green: Byte read GetGreen write SetGreen;
property Blue: Byte read GetBlue write SetBlue;
property Cyan: Byte read GetCyan write SetCyan;
property Magenta: Byte read GetMagenta write SetMagenta;
property Yellow: Byte read GetYellow write SetYellow;
end;
implementation
procedure TBasicColor.Assign(const ColorRef: TColor);
begin
if (ColorRef < 0) or (ColorRef > $FFFFFF)
then raise ERangeError.Create('ERangeError in TBasicColor class.' + sLineBreak +
'It supports only subset of TColor range.' + sLineBreak +
'Valid TBasicColor range is <0; $FFFFFF>.')
else FColorRef := ColorRef;
end;
function TBasicColor.RGBToColor(const Red, Green, Blue: Byte): TColor;
begin
Result := Red or (Green shl 8) or (Blue shl 16);
end;
constructor TBasicColor.Create(const ColorRef: TColor);
begin
// in here it is just plain assignment
inherited Create;
Assign(ColorRef);
end;
constructor TBasicColor.Create;
begin
// in case anyone just calls Create() we assign white color
Create($FFFFFF);
end;
constructor TBasicColor.CreateRGB(const Red, Green, Blue: Byte);
begin
Create(RGBToColor(Red, Green, Blue));
end;
constructor TBasicColor.CreateCMY(const Cyan, Magenta, Yellow: Byte);
begin
CreateRGB(255 - Cyan, 255 - Magenta, 255 - Yellow);
end;
constructor TBasicColor.CreateRandom;
begin
Create(Random($FFFFFF + 1));
end;
function TBasicColor.GetColorRef: TColor;
begin
Result := FColorRef;
end;
procedure TBasicColor.SetColorRef(const ColorRef: TColor);
begin
Assign(ColorRef);
end;
function TBasicColor.GetRed: Byte;
begin
Result := Byte(FColorRef);
end;
procedure TBasicColor.SetRed(const NewRed: Byte);
begin
Assign(RGBToColor(NewRed, GetGreen, GetBlue));
end;
function TBasicColor.GetGreen: Byte;
begin
Result := Byte(FColorRef shr 8);
end;
procedure TBasicColor.SetGreen(const NewGreen: Byte);
begin
Assign(RGBToColor(GetRed, NewGreen, GetBlue));
end;
function TBasicColor.GetBlue: Byte;
begin
Result := Byte(FColorRef shr 16);
end;
procedure TBasicColor.SetBlue(const NewBlue: Byte);
begin
Assign(RGBToColor(GetRed, GetGreen, NewBlue));
end;
function TBasicColor.GetCyan: Byte;
begin
Result := 255 - GetRed;
end;
procedure TBasicColor.SetCyan(const NewCyan: Byte);
begin
SetRed(255 - NewCyan);
end;
function TBasicColor.GetMagenta: Byte;
begin
Result := 255 - GetGreen;
end;
procedure TBasicColor.SetMagenta(const NewMagenta: Byte);
begin
SetGreen(255 - NewMagenta);
end;
function TBasicColor.GetYellow: Byte;
begin
Result := 255 - GetBlue;
end;
procedure TBasicColor.SetYellow(const NewYellow: Byte);
begin
SetBlue(255 - NewYellow);
end;
initialization
Randomize;
end.