Jump to content

Primož Gabrijelčič

Members
  • Content Count

    246
  • Joined

  • Last visited

  • Days Won

    11

Posts posted by Primož Gabrijelčič


  1. Hi all,

     

    Somehow I think that the following code should compile, but it does not. It looks like the compiler uses the wrong `TCallback` definition when resolving the property. Am I correct or did I miss something and I'm just stupid?

     

    program Project184;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils;
    
    type
      TCallback = reference to procedure (value: int64);
    
      TRunner = class
      public type
        TCallback = reference to procedure;
      strict private
        FOnCallback: TCallback;
      public
        procedure Run;
        property OnCallback: TCallback read FOnCallback write FOnCallback;
      end;
    
    procedure TRunner.Run;
    begin
      OnCallback(); // <-- E2035 Not enough actual parameters
    end;
    
    begin
    end.


    Delphi 10.3.1, in case this is a regression bug.


  2. Tricky, got me completely dazzled for a moment 🙂

     

    You are now generating new capture proc for each `I`. That `Proc`, however, is just a pointer. Now your code `Task.Invoke(procedure begin Proc(); end);` captures this `Proc` by value and executes the last stored value three times. 

     

    You should do it like this:

     

    function CaptureValue(Value: Integer): TOmniTaskInvokeFunction;
    begin
      Result := procedure begin Memo.Lines.add(Value.ToString); end;
    end;
    
    for I := 0 to 2 do
      begin
        Task.Invoke(CaptureValue(i));
      end;

     

    • Thanks 1

  3. If I understand correctly, this has nothing to do with a pipeline. a) You "create a protocol" (whatever that means) b) You submit that protocol to a background thread to execute it and c) you repeat step b. And you can do all that with different protocols.

     

    As one protocol is a normal serial process, it has nothing to do with threading. Just implement "protocol runner" as a state machine. 

     

    Then create a Background Worker abstraction and submit it work items. One work item = one "protocol runnner".

     

     

    • Thanks 1

  4. I don't really understand your questions, sorry.

     

    If you run some external code, you cannot be sure if it is thread-safe. You should protect access to it with some synchronization mechanism. 

     

    A whole idea of a guardian object with a "not responding" callback is new to me. I don't believe it is a commonly used pattern. I can't comment on it as I never used it in practice.


  5. By debugging, of course. Although, I have to admit, it had me quite stumped for a while.

     

    A bit of logging proves that service start and stop both come from the same thread (the first number is the thread id):

     

    8996|ServiceCreate
    8996|Servizio attivato porta = 8889
    17284|ServiceStart
    8996|Got message 1024
    8996|Keep Alive
    8996|Got message 1024
    8996|Keep Alive
    17284|A try to stop task...
    6456|Sending COmniTaskMsg_Terminated
    17284|FRunner.ExitCode = 0
    17284|ServiceStop
    8996|Got Terminated
    8996|OnTaskTerminated?
     

    8996 is the main service thread, 17284 is the service control thread, 6456 is the thread running the background task.

    

    • Thanks 1

  6. Your task is created in one thread (main service thread) and that thread processes messages sent from the task, including the termination message.

     

    The task is stopped from a different thread. ServiceCreate and ServiceStart/ServiceStop are NOT executed from the same thread.

     

    The OtlTaskControl code expects that the task will be terminated from the same thread that it was created in. The .Terminate method therefore tries to process all messages waiting in the message queue (for example, the termination message), but it cannot do that as the message queue is not associated with the "ServiceStop" thread but with the "ServiceCreate" thread.

     

    After the ServiceStop exits, the main service thread is destroyed and that destroys the IOmniTaskControl object. At that moment, messages from the inter-thread message queue are processed, but they cannot be dispatched anywhere as the main service thread is being terminated. (Because of a DSiWin32.pas bug, messages will still be dispatched and may cause the program to crash. This will be fixed today.)

     

    Solution? Just do your OnTerminated processing in ServiceStop.

     


  7. I can't tell the reason for your problem from the example you've given. Please post a minimal, compilable, and complete project that exhibits the problem and I'll look into it. It would be also good if you can drop dependency on WebBroker when preparing that project.


  8.  

    Earlier this week a long-time customer asked me why FastMM allocates large memory blocks from the top of the memory and if that option could safely be turned off. Their reasoning was that such allocations are much slower than normal ones. The question surprised me as I didn’t know of any such difference in speed so I did what I usually do–I wrote a benchmark application and measured it.
    TL;DR Yes, allocating from the top is slower. No, the difference is not big and in most cases you’ll not even notice it.
    There were, however, other interesting results that my simple benchmark pointed out. More on that in a moment, but first…

    Allocating from bottom and top

    In Windows, the code can ask for a memory block by calling VirtualAlloc with flag MEM_COMMIT and Windows will give you a suitable chunk of memory. This memory will usually be allocated from the start of the virtual memory visible to the program.

    The code can, however, call VirtualAlloc with flag MEM_COMMIT OR MEM_TOP_DOWN and Windows will return a block from the end of virtual memory available to the process. In a typical 32-bit Delphi program first such memory block will have address close to $7FF00000 (but a bit lower).

    You may want to allocate memory “from the top” if your program has two very distinct modes of allocating memory and you don’t want to mix them. For example, a frequently reallocated memory could come “from the bottom” and large blocks that are used for long periods of time “from the top”. This can reduce memory fragmentation, but the potential advantages are specific to each program. In other words – maybe it will help, maybe it will hurt.

    Another good scenario for MEM_TOP_BOTTOM is testing 64-bit code ported from 32-bits. For example, a typical “from the top” allocated block in a 64-bit program will have an address like this: $7FF4FDE30000. If your code at some point stores pointers into 4-byte integers, part of the address will be lost and as soon as that integer is converted back into a pointer and the code accesses that pointer, you’ll quite probably get an access violation. If a memory comes “from the bottom”, such problems would not be so easily detected.

    FastMM4 allocates large blocks (with sizes greater or equal to 258.5 KB) “from the top”. If I recall correctly, this was done to prevent memory fragmentation. Additionally, it can allocate all other block “from the top” if you define conditional symbol AlwaysAllocateTopDown and rebuild. (You have to use FastMM4 from github instead of the built-in Delphi version to use this functionality.) You can use this mode to test 32-bit programs ported to 64-bit code.

    MEM_TOP_DOWN is slower?

    The article my customer pointed to claimed that allocating from the top works much slower than allocating from the bottom. Even worse, the allocation algorithm was supposed to work in O(n^2) time so each additional allocation needs more time to execute. To top that off, the official documentation for the MEM_TOP_DOWN flag mentions:
    This can be slower than regular allocations, especially when there are many allocations.
    To verify that claim, I wrote a trivial benchmarking app (download it here). It allocates from 1 to 6000 blocks of size 264752 and measures the time needed. Block size 264752 was picked because at that size FastMM4 starts allocating memory “from the top”. 6000 blocks can safely be allocated in a 32-bit application (6000 * 264752 = 1.5 GB). In my tests I could allocate 6105 such blocks before I ran “out of memory” but just to be on the safe side I reduced the number in the released application.

    Results, measured on my fresh new notebook with a i7-8750 processor, were much closer to my expectations than to some O(n^2) algorithm. The “Top” algorithm is slightly slower (needs more time to execute) but the difference is not drastically large.
     
    image.thumb.png.472256bd6a52dafd8db04ff760d7566a.png

    What’s going on then? Is MEM_TOP_DOWN slow or not?

    As it turned out, the article I was referring to was written in 2011 and Windows have improved a lot since then. I don’t know which Windows version has fixed the “top allocation” problem, but it definitely doesn’t appear in Windows 7 and 10.

    Another interesting result is that the first 200 MB (approximately) are almost “free”. Somewhere around that number, the execution time jumps from around 3 ms to 50 ms and then continues to grow in more-or-less linear fashion. The benchmarking program measures each test only once and is therefore very susceptible to measurement errors but the result clearly shows an O(n) algorithm.

    Why are allocations smaller than 200 MB so fast? I’m guessing that Windows maps such amount of physical memory into the process’ virtual space when the process is started. When you exceed that limit, the allocator needs more time to allocate physical memory and map it into the process’ virtual space. That’s, however, just a guess. If you know better, please let me know in the comments.

    How fast are YOUR allocations?

    Just for the sake of completeness I rerun tests on my main PC (HP z820 with two E5 Xeons) and the results completely surprised me.
     
    image.thumb.png.6c9bb3488f7aaad4bdc059e1246e42d9.png

    The shape of the curve is almost the same–but notice the difference in speed! On the laptop, 4000 allocations execute in 250 ms. On the Xeon machine, over 1000 ms is needed for the same job.

    This machine is quite old (around 4 years IIRC) and it obviously contains a much slower memory. I know that computers can have faster or slower memory chips, but I never expected to see such a big difference in VirtualAllocspeed. (And yes, both machines are running latest Windows 10.)

    Now the whole shebang started to interest me even more, and I did some further tests on a few PCs used by fellow programmers. All of them were running Windows 10. As you can see below, there is some difference between them but none are so slow than my main computer 😞 Maybe the time has come to upgrade…
     
    image.thumb.png.79a235531a31ec6b587f87003ea49515.png

    If you want to download raw data and compare it to your own results, you can access it here.

    MEM_TOP_DOWN or not?

    The difference in speed is not that big–and most programs will not notice it–but I have to agree with the customer. The time has come to remove hard-coded MEM_TOP_DOWN from FastMM4 and replace it with a conditional {$IFDEF AllocateLargeBlocksTopDown}MEM_TOP_DOWN{$ENDIF}.

     

    I have created pull request for that change: https://github.com/pleriche/FastMM4/pull/75

     

    (Original blog post: https://www.thedelphigeek.com/2019/04/fastmm4-large-memory.html)

    • Like 2
    • Thanks 3

  9.   FWorker := TWorker.Create(SpinEdit1.Value);
    
      FTimedTask :=
        Parallel.TimedTask .Every(1000)
          .Execute(
            procedure (const task: IOmniTask)
            var
              value: integer;
            begin
              FWorker.Tick;
              value := FWorker.Value;
              task.Invoke(
                procedure
                begin
                  ListBox1.Items.Add(value.ToString);
                end);
            end);
    
      FTimedTask.Start;
    
    type
      TWorker = class
      strict private
        FValue: integer;
      public
        constructor Create(startAt: integer);
        procedure Tick;
        property Value: integer read FValue;
      end;
    
    constructor TWorker.Create(startAt: integer);
    begin
      inherited Create;
      FValue := startAt;
    end;
    
    procedure TWorker.Tick;
    begin
      Inc(FValue);
    end;

     

    Source: https://gist.github.com/gabr42/15a514a2960be43a0a5b1a6db69274f7

     

    • Like 2

  10. While writing Design Patterns with Delphi, I spent quite some time researching existing literature on design patterns implementation in Delphi, design patterns in other languages, other types of patterns, design principles and so on …

     

    In case you would like to dig deeper than the book takes you, here is my reading list.

    Design Patterns Essentials

    Software design pattern

    Computer Science Design Patterns

    The 23 patterns from the GoF

    Design Patterns with examples in different languages (including Delphi)     

    Gang of Four Design Patterns Reference Sheet

    Programming principles

    Design principles and design patterns

    SOLID

    Don't Repeat Yourself (DRY)

    KISS

    YAGNI (You Ain't Gonna Need It)

    Software Design Patterns Are Not Goals, They Are Tools

    I'm Sick Of GoF Design Patterns

    Software Development AntiPatterns

    Big Ball of Mud

    Design Patterns for Humans

    Spring4D
       
    MMX Code Explorer

    Singleton

    https://en.wikipedia.org/wiki/Singleton_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Singleton

    http://www.yanniel.info/2010/10/singleton-pattern-delphi.html

    https://ibeblog.com/2010/08/18/delphi-singleton-patterns/

    https://stackoverflow.com/a/1409672/4997

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Creational.Singleton

    https://stackoverflow.com/questions/1409593/creating-a-singleton-in-delphi-using-the-new-features-of-d2009-and-d2010

    https://sourcemaking.com/design_patterns/singleton

    https://schellingerhout.github.io/design%20patterns/design-patterns-creational-delphi/

    https://github.com/kamranahmedse/design-patterns-for-humans#-singleton

    Dependency injection 

    https://en.wikipedia.org/wiki/Dependency_injection

    http://www.nickhodges.com/post/Service-Locator-is-Indeed-an-Anti-pattern.aspx

    https://softwareengineering.stackexchange.com/questions/135914/why-was-dependency-injection-pattern-not-included-in-the-gang-of-four/135982

    https://stackoverflow.com/questions/4176520/what-is-the-difference-between-strategy-pattern-and-dependency-injection

    Lazy Initialization

    https://en.wikipedia.org/wiki/Lazy_initialization

    Object pool

    https://en.wikipedia.org/wiki/Object_pool_pattern

    https://sourcemaking.com/design_patterns/object_pool

    Factory method

    https://en.wikipedia.org/wiki/Factory_method_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Factory_method    

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Creational.FactoryMethod                     

    https://sourcemaking.com/design_patterns/factory_method

    https://schellingerhout.github.io/design%20patterns/design-patterns-creational-delphi/

    https://github.com/kamranahmedse/design-patterns-for-humans#-factory-method

    Abstract factory

    https://en.wikipedia.org/wiki/Abstract_factory_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Abstract_Factory

    http://www.felix-colibri.com/papers/design_patterns/factory_and_bridge_patterns/factory_and_bridge_patterns.html#abstract_factory_pattern

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Creational.AbstractFactory

    http://www.nickhodges.com/post/Delphi-and-the-Factory-Pattern-Simple-Factory.aspx

    https://sourcemaking.com/design_patterns/abstract_factory

    https://schellingerhout.github.io/design%20patterns/design-patterns-creational-delphi/

    https://github.com/kamranahmedse/design-patterns-for-humans#-abstract-factory

    Prototype

    https://sourcemaking.com/design_patterns/prototype

    https://schellingerhout.github.io/design%20patterns/design-patterns-creational-delphi/

    https://en.wikipedia.org/wiki/Object_copying#Deep_copy

    https://github.com/kamranahmedse/design-patterns-for-humans#-prototype

    Builder

    https://en.wikipedia.org/wiki/Builder_pattern

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Creational.Builder

    https://sourcemaking.com/design_patterns/builder

    https://schellingerhout.github.io/design%20patterns/design-patterns-creational-delphi/

    https://github.com/kamranahmedse/design-patterns-for-humans#-builder

    Composite

    https://en.wikipedia.org/wiki/Composite_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Composite

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Structural.Composite

    https://sourcemaking.com/design_patterns/composite

    https://github.com/kamranahmedse/design-patterns-for-humans#-composite

    Flyweight

    https://en.wikipedia.org/wiki/Flyweight_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Flyweight

    https://sourcemaking.com/design_patterns/flyweight

    https://github.com/kamranahmedse/design-patterns-for-humans#-flyweight

    https://en.wikipedia.org/wiki/String_interning   

    Marker interface

    https://en.wikipedia.org/wiki/Marker_interface_pattern

    Bridge

    https://stackoverflow.com/questions/350404/how-do-the-proxy-decorator-adapter-and-bridge-patterns-differ

    https://en.wikipedia.org/wiki/Bridge_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Bridge

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Structural.Bridge

    https://sourcemaking.com/design_patterns/bridge

    http://www.felix-colibri.com/papers/design_patterns/factory_and_bridge_patterns/factory_and_bridge_patterns.html#the_bridge_design_pattern

    https://simpleprogrammer.com/design-patterns-simplified-the-bridge-pattern/

    https://github.com/kamranahmedse/design-patterns-for-humans#-bridge

    https://stackoverflow.com/q/319728/4997

    Adapter

    https://en.wikipedia.org/wiki/Adapter_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Adapter

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Structural.Adapter      

    https://sourcemaking.com/design_patterns/adapter

    https://github.com/kamranahmedse/design-patterns-for-humans#-adapter

    Proxy

    https://en.wikipedia.org/wiki/Proxy_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Proxy

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Structural.Proxy

    https://sourcemaking.com/design_patterns/proxy

    https://github.com/kamranahmedse/design-patterns-for-humans#-proxy

    Decorator

    https://en.wikipedia.org/wiki/Decorator_pattern

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Structural.Decorator

    https://sourcemaking.com/design_patterns/decorator

    https://github.com/kamranahmedse/design-patterns-for-humans#-decorator

    Facade

    https://en.wikipedia.org/wiki/Facade_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Facade

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Structural.Facade

    https://sourcemaking.com/design_patterns/facade

    https://github.com/kamranahmedse/design-patterns-for-humans#-facade

    Null object

    https://en.wikipedia.org/wiki/Null_object_pattern

    https://sourcemaking.com/design_patterns/null_object

    Template method

    https://en.wikipedia.org/wiki/Template_method_pattern

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Behavioral.TemplateMethod

    https://www.codeproject.com/Articles/516094/TemplateplusMethodplusDesignplusPatternplusinplusD

    https://sourcemaking.com/design_patterns/template_method

    https://github.com/kamranahmedse/design-patterns-for-humans#-template-method

    Command

    https://en.wikipedia.org/wiki/Command_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Command

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Behavioral.Command

    https://sourcemaking.com/design_patterns/command

    https://github.com/kamranahmedse/design-patterns-for-humans#-command

    https://stackoverflow.com/q/6064116/4997

    State

    https://en.wikipedia.org/wiki/State_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/State

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Behavioral.State

    https://sourcemaking.com/design_patterns/state

    https://sourcemaking.com/design_patterns/state/delphi

    https://github.com/kamranahmedse/design-patterns-for-humans#-state

    https://en.wikipedia.org/wiki/Finite-state_machine

    Iterator

    https://en.wikipedia.org/wiki/Iterator_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Iterator

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Behavioral.Iterator

    https://sourcemaking.com/design_patterns/iterator

    https://github.com/kamranahmedse/design-patterns-for-humans#-iterator

    http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Declarations_and_Statements_(Delphi)#For_Statements

    Visitor

    https://en.wikipedia.org/wiki/Visitor_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Visitor

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Behavioral.Visitor

    https://sourcemaking.com/design_patterns/visitor

    https://sourcemaking.com/design_patterns/visitor/delphi     

    https://github.com/kamranahmedse/design-patterns-for-humans#-visitor   

    Observer

    https://en.wikipedia.org/wiki/Observer_pattern

    https://en.wikipedia.org/wiki/Publish–subscribe_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Observer

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Behavioral.Observer

    https://github.com/spinettaro/delphi-event-bus

    https://sourcemaking.com/design_patterns/observer/delphi

    https://github.com/kamranahmedse/design-patterns-for-humans#-observer

    http://docwiki.embarcadero.com/Libraries/en/System.Messaging.TMessageManager

    http://docwiki.embarcadero.com/RADStudio/en/Using_the_RTL_Cross-Platform_Messaging_Solution

    http://docwiki.embarcadero.com/RADStudio/en/Sending_and_Receiving_Messages_Using_the_RTL

    http://docwiki.embarcadero.com/Libraries/en/System.Classes.TComponent.Observers

    Memento

    https://en.wikipedia.org/wiki/Memento_pattern

    https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Memento

    https://github.com/jimmckeeth/DelphiPatterns/tree/master/Behavioral.Memento

    https://sourcemaking.com/design_patterns/memento

    https://github.com/kamranahmedse/design-patterns-for-humans#-memento

    Lock

    https://en.wikipedia.org/wiki/Lock_(computer_science)

    https://en.wikipedia.org/wiki/Spinlock

    Lock striping

    https://netjs.blogspot.si/2016/05/lock-striping-in-java-concurrency.html

    Double-checked locking

    https://en.wikipedia.org/wiki/Double-checked_locking

    https://en.wikipedia.org/wiki/Test_and_test-and-set

    Optimistic locking

    https://www.javaworld.com/article/2075406/java-web-development/optimistic-locking-pattern-for-ejbs.html

    https://martinfowler.com/eaaCatalog/optimisticOfflineLock.html

    https://en.wikipedia.org/wiki/Optimistic_concurrency_control

    Readers-writer lock

    https://en.wikipedia.org/wiki/Readers–writer_lock

    https://docs.microsoft.com/en-us/windows/desktop/sync/slim-reader-writer--srw--locks

    Thread pool

    https://en.wikipedia.org/wiki/Thread_pool

    https://stackoverflow.com/questions/47504201/delphi-ttask-and-tthreadpool

    Messaging

    https://en.wikipedia.org/wiki/Message_passing

    https://en.wikipedia.org/wiki/Message_queue

    Future

    https://en.wikipedia.org/wiki/Futures_and_promises

    Pipeline

    https://en.wikipedia.org/wiki/Staged_event-driven_architecture

    Designing Delphi programs

    https://en.wikipedia.org/wiki/Event-driven_programming

    http://blong.com/Articles/Actions/Actions.htm

    https://github.com/andrea-magni/TFrameStand

    https://martinfowler.com/eaaCatalog/tableModule.html     

    https://stackoverflow.com/questions/433819/table-module-vs-domain-model 

    http://docwiki.embarcadero.com/RADStudio/en/LiveBindings_in_RAD_Studio

    https://www.embarcadero.com/images/dm/technical-papers/understanding_rad_studio_livebindings.pdf

    Other kinds of patterns

    https://en.wikipedia.org/wiki/Exception_handling

    http://wiki.c2.com/?ExceptionPatterns

    http://docwiki.embarcadero.com/Libraries/en/System.TObject.Destroy

    http://www.heaventools.com/eurekalog-exception-logger.htm

    http://www.madshi.net/madExceptDescription.htm

    https://github.com/project-jedi/jvcl

    https://en.wikipedia.org/wiki/Debugging_patterns

    https://git-scm.com/docs/git-bisect

    http://www.washi.cs.waseda.ac.jp/wp-content/uploads/2017/03/Woei-Kae-Chen.pdf

    https://github.com/colinj/Functional

    https://fsharpforfunandprofit.com/fppatterns/

    https://en.wikipedia.org/wiki/Comparison_of_programming_paradigms

    Books & papers

    Architectural Patterns, Pethuru Raj, Anupama Raman, Harihara Subramanian

    Coding in Delphi, Nick Hodges

    Concurrency with Modern C++, Rainer Grimm

    Concurrent Patterns and Best Practices, Atul S. Khot

    Dependency Injection in Delphi, Nick Hodges

    Design Patterns for Humans, Kamran Ahmed

    Design Patterns Past and Future, Aleksandar Bulajic

    Design Patterns - Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides

    Dive Into Design Patterns, Alexander Shvets

    Java Design Patterns, Devendra Singh

    More Coding in Delphi, Nick Hodges

    The Null Object Pattern, Bobby Woolf

    • Like 8
    • Thanks 20
×