-
Content Count
1237 -
Joined
-
Last visited
-
Days Won
25
Everything posted by David Schwartz
-
How to switch condition position?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Rudy said: I think this order is guaranteed, but I would not really count on it. There's a compiler switch that overrides this, so it's not really "guaranteed". -
How to switch condition position?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
There's always the IfThen function. Result := IfThen( aStr = 'OK', True, False ); The problem is, this form If (condition) Then Result := True; does not set Result if condition is false. So it can be undefined or random. -
Should I create subclass for User interface interaction?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Better. Now, refactor this so the implementation of Arguments is not visible anywhere other than inside of a constructor somewhere. Again, the whole idea of encapsulation is to hide the implementation of data so it can change without affecting how it's used by clients. IOW, if you were to change it from TArray to TList or TDictionary, none of your application code would be affected. -
Should my record be a class instead?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Gotcha. My mistake. -
Should I create subclass for User interface interaction?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
That looks a LOT better! I'd fill out the constructors. The purpose of a ctor is to initialize the state of an object. When it's a simple item like what you've got, there might be multiple ctors: one would take no arguments and return an empty object, but initialize the contents anyway just to be safe. The other(s) would offer different combinations of parameters to allow you to create and set values in one statement. Instead, you create an empty object that's not initialized, then assign values to it separately. While that's fine, it's extra coding that can be eliminated. Also, you seem addicted to using TArray, even though the way you're using it is more like a TList. You never set the initial size anywhere, which appears to be unbounded. So why not use a TList instead? In ParseFile, you don't need try...finally because you don't want to delete the object you just created b/c it's being added to a list, as your comment suggests. If you delete it, you'll end up with a list full of pointers to freed objects. Consider this instead of that whole block: AddNode( TABCStructureNode.Create( 25, 'parent_of_25', 'contents of node 25' ) ); But, if you stare at that long enough, you start to think, "why not just do this instead?" AddNode( 25, 'parent_of_25', 'contents of node 25' ); where the TABCStructureNode is created implicitly by AddNode -
Should my record be a class instead?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
records and classes are different. Classes already have destructors. -
Should I create subclass for User interface interaction?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
I did not show the class definition on purpose. So that's only implied. Why don't you try writing the class definition (interface part) from what I've shown here so far? -
Should I create subclass for User interface interaction?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
It seems you've got some heuristics in your head that are confusing you. There are no such rules. I suspect folks here could come up with a half-dozen different approaches. This is my approach. None of them is "the right approach" if they all work. I didn't put in any properties just to keep things simple for you. But you're welcome to add them and hide the explicit Get/Set methods. OOP has three legs: Encapsulation, Inheritance, polymorphism. Virtually all OOP situations involve Encapsulation; the others are only used if/when needed. All I've done is employ encapsulation. The others are not needed. In your approach, your encapsulation was overly complex and you kept trying to introduce unnecessary inheritance and composition. If you simply use a TStringlist, you don't need a class. But if you do that, all of your interactions require the client code to know exactly what your implementation is -- a TStringlist of TStringlists. Or an array of TUPLEs (or 2-member records). By encapsulating your implementation inside of a class, you can present a simple interface to the clients and completely hide the implementation. It doesn't matter if it's just one string or an entire frigging database system in the cloud with two levels of fall-back in case of failure. The client just sees one simple interface. And if you change the implementation, you don't have to change the way any other code deals with this class. That's the whole goal. -
Should I create subclass for User interface interaction?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Whoa. Spaghetti AND meatballs. 😮 Do NOT mix UI elements with data classes! I don't have time to unravel this mess for you. I'd start with your basic "item", which seems to be something like a TStringlist plus a name (that record thing). This could be a TUPLE of some sort. Alternatively, you could use the first entry of the stringlist to contain its own name, simply to avoid the additional structure just to have a separate identifier for it. But it may not even be necessary ... hang on. Your second level is you have a collection of these items. You use a TArray, but it could be a TList or even a TCollection. Whatever. It needs Add, Insert, Get, and Delete methods, and maybe a lookup of some kind (so you can find an "Item by name"). However, because of the simplicity of this, you could use one main TStringlist where the Strings[n] element is a 'name' and Objects[n] is a TStringlist. Then sl.IndexOf(some_name) -> n, and sl.Objects[n] -> the TStringlist (from your record). Viola! No need for a record or even an array. Just one TStringlist. Why use multiple structures when one will work fine for all needs? Technically speaking, while you could get by with just a single TStringlist, I'd wrap it into a class just to provide a simpler and more appropriate interface for it (eg., so you don't know how it's actually implemented). Memo.Lines is of type TStrings, so you'd want methods like these to interface nicely with TMemos: function TmyClass.GetItem( const nm : string ) : TStrings; var n : integer; begin Result := NIL; n := sl.IndexOf(nm); // assums 'sl' is the name of the main TStringlist in this object if (n >= 0) then Result := TStrings( sl.Objects[n] ) end; procedure TmyClass.SetItem( const nm : string; Value : TStrings ); var n : integer; sl2 : TStringlist; begin sl2 := GetItem(nm); if Assigned(sl2) then sl2.Assign(Value) else begin sl2 := TStringlist.Create(); sl2.Assign(Value); sl.AddObject( nm, sl2 ); end; end; Then when you need to get an item's value to a Memo, you'd use: memo1.Lines.Assign( myClassObj.GetItem( some_name ) ); // maybe do GetItem first to check for NIL before calling Assign in case Assign chokes if you give it NIL and to save the memo into an item buffer, you'd use: myClassObj.SetItem( some_name, memo1.Lines ); See how you're completely hiding the implementation, while providing an interface that makes using it very simple? And it knows nothing about UI elements, and yet works very simply with them. The class TmyClass is actually very simple, as it just contains a TStringlist (named 'sl' above). (Not DERIVED from it, but CONTAINS it.) The constructor would create it, and the destructor would destroy it. You'd have GetItem, SetItem, and maybe one or two other methods. That's it. -
I must have missed the memo on this, so I'm wondering how to deal with it. I have some local DB tables that I access with my app and all works fine. Then I decided to move the tables to a DB server. Oops. Performance issues arise. I'm using SQL components that double as Tables, so I've got to put in a SQL statement like 'select * from xyz' to get the data to load. That's fine except a couple of tables have BLOB fields that slow things down significantly when they're moved to the server. So I split the BLOB fields out into a separate table and I load them up as-needed (whenever the main table's record changes). This works fine, but it seems a bit convoluted. What I'm wondering is ... when you've got controls like listviews that only show a dozen or so lines, how do you paginate the result sets so it only loads up the records being viewed? Also, how do you NOT load up things like BLOBs unless you actually want to see them? (Since I pre-defined the tables with the fields so I can refer to field vars in the code rather than using FieldByName for everything, if I don't do a 'select * from...' then an error arises saying a data field is missing.) It would seem that the controls would need to work in conjunction with the queries to manage paging properly. There's a DBListBox in the standard palette, but no DBListView. In this case, I'm using DevExpress cxGrids, and I suspect they have a way to do this but I didn't really look into it. Do LiveBindings help solve this problem vs. regular data-aware controls? (I haven't really played with them yet.) Otherwise, how do you set up a basic pagination scheme for fetching small chunks of data from the server based on what's being viewed?
-
General DB access question -- paging query results
David Schwartz replied to David Schwartz's topic in Databases
We're using Oracle 10g. -
Should my record be a class instead?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
When you pass everything as a string, as this guy seemed to be doing, you probably do. Or you need lots of exception handling code to deal with conversion errors. -
how to load updated component code w/o rebuilding lib?
David Schwartz posted a topic in Delphi IDE and APIs
I've got a 3rd-party library and found a bug in it. The vendor fixed the bug and sent me two files they updated. It's not very practical to rebuild these libs, so I want to just load the files into the project folder and use them directly until they issue them in a formal library update. Nothing I've tried is working. I know there's a way, but I guess I'm forgetting something. What's the correct way to do this? Note that these are both for visual components. (One is for DB use, one is not, and I use them both.) There's a version property in the components that shows the version, and the newer files have a newer version#, and it's not what's being displayed. -
version control system Version Control System
David Schwartz replied to Soji's topic in Delphi IDE and APIs
I suggest git with GitHub or GitLab. I use Tower.- 49 replies
-
- git
- subversion
-
(and 1 more)
Tagged with:
-
Given that all you need to make Delphi work on Centos is changing 9 search paths ... that's very encouraging! Can the SDK be modified to do that for each Linux project you create after that? Or will the updates be needed every time you set up a new project in that platform?
-
Should my record be a class instead?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
In another post you showed something that's got some kind of TProject thing. You said it's a rather large structure. From what you showed, I'm guessing that you're also not allocating your records from the heap using new() because that would require you to call free() or whatever it's called. (I rarely use records, sorry.) So you've got arrays of huge records sitting in the global namespace and you're wondering why various debugging tool sare having trouble working. Have you considered that they're optimized to work with smaller chunks of things allocated from the heap at run-time? What percentage of your records are allocated from the heap? And where are all of these structures being loaded up? From a database? An INI file? Or just stuff collected at run-time? Where does it go when the program shuts down? To me, OOP is simply a way of organizing things at the source code level to make everything easier to manage. What you end up with (should be) very clear and organized. (I've seen designs that are far from it, but that's the general goal.) There are some constructs in source code that have zero impact on memory and/or execution time, so source code often appears somewhat bloated compared to non-OOP code, although a lot of it has no impact on anything. (Properties that refer to fields directly rather than via setter/getter methods, for example.) What most people focus on are the handful of things that are invisible at the source code level that DO have an impact on memory and/or execution time. This is penny-wise and pound-foolish most of the time. In your case, you just don't seem to see the value of having a tackle box to keep your tackle in rather than a toolbox, and prefer to put into whatever random bags and boxes you have around. And you put your snacks and first aid stuff into the same random bags and boxes. You're confused because a "tackle box" would seem to be derived from a "box". So what? So you seem to prefer a less organized solution instead. Or maybe a toolbox with woodworking tools, or another one with auto mechanic tools. Or maybe a matchbox with matches in it. You are pretending that these are all the same types of containers, that their contents have no particularly differentiating factors, and it's fine to put them into whatever bag or box or bottle is handy. Just don't put matches or tools into something that may get wet. Do you put gasoline into a container that isn't designed to hold liquids? Or flammable liquids? Wait, those are properties that belong to containers. But you're just using random bags, boxes, and bottles. Without having differentiating properties, any of them will do for any purpose. That's what you're saying. You could use a zip-lock baggie for your tackle and put your soda into a matchbox. What you don't realize is THAT approach means MORE CODE is needed to sift through all of the conflicting properties that should be IMPLICIT in the CONTAINERS they're in. Do you really enjoy writing all of that code? Do you realize how much of it is needed? I imagine not. A tackle box is a box; a matchbox is a box; a toolbox is a box. And you refuse to use any of them because you think the fact that they're all "boxes" and share a few common properties is ... confusing? Would you put a liquid in a box? Well, if it has a property like "can_hold_liquid" that's set to true. I have only one question for you: if you don't want to organize things in such an intuitive way, how are you managing all of this now? I guarantee you it is NOT invisible! It's just part of the mess that your code has to deal with. You don't put fishing tackle in a matchbox. But if they're each just a "box" then you need code added to each one that is intended to insure that what you put there is acceptable. Polymorphism is only confusing when you THINK you only have one kind of box in this case. Since everything is just a "box", you've probably got lines and lines of Assert statements or nested if...then...else clauses checking to see if stuff is safe to add and update. The reason is that by using properly designed classes, the COMPILER tells you when you try adding the wrong objects to things. You know the old saying: "when your only tool is a hammer, everything looks like a nail"? When you aren't using classes, your only tool might as well be a hammer, because every piece of code you write needs to check to see if the thing really IS a nail or not. Simple question for you: how much of your source code is doing nothing but checking to be sure the parameters being passed are of the correct type and/or within a legal range? If you switch to an OOP design, 99% of that code will disappear because the compiler will catch it. And I promise you that all of that extra code and complexity is increasing your maintenance overhead and technical debt considerably. -
Should my record be a class instead?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
@Rudy Velthuis While I agree in principle, this really has nothing to do with initializing and tearing down objects. You're still going to need to write code to DO the initialization and destruction, and then put it inside of specific methods (constructors and destructors, presumably) in order for the automated setup and tear-down logic to work, right? Whatever style of programming you use, some run-time variables WILL always need to be initialized at run-time. Whether you put them into a constructor that's called automatically or in some random function that you call to initialize global variables is irrelevant. Just don't say you dislike having to call a method to initialize your variables in one case, because it's required in ALL cases. Some just aren't as convenient. By convention, constructors in Delphi are named Create. In fact, you can name them whatever you want, even "PrepareGlobalRec" or whatever. Just change "procedure PrepareGlobalRec" to "constructor PrepareGlobalRec" and you've got a class constructor! And in many cases, you don't even need to call it. He has not saved a single line of code by avoiding using a class, but in some cases he has by using classes. The initialization code is required no matter what. But without any constructor at all, there's nothing for the memory manager to call to automatically create anything. -
Is it really good practice to create Forms only as needed? Always?
David Schwartz replied to Mike Torrettinni's topic in VCL
Well ... I'd think more like 5 minutes per tab to copy the components into a new form. Then you need to figure out how you're going to get the data in and out, which should be the same for all of your forms (just for consistency sake). So the first four might take an hour. But after they, they'll go steadily quicker, maybe 10 minutes each. Watch my CodeRage 9 video for more insights: https://www.youtube.com/watch?v=qqKx8fQTTfI -
Should my record be a class instead?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
When you eat something, you need to prepare it (maybe cook it, or order it, or unwrap it), then you need to clean up afterwards. When you get in your car to go somewhere, you need to start it up; when you arrive at your destination, you need to shut it off. When you walk into a room, you may need to open the door and perhaps turn on a light; when you leave, you may need to shut the light off and close the door. Most things in life require some kind of setup, then you do something, then you clean up after yourself. Tell me it really bothers you having to deal with this constantly in your life ... or if you're like most people, you don't ever give any of it a second thought. Objects work the same way. The fallacy in your logic (and people who claim to dislike creating and freeing things) is that just because there's nothing named "constructor" or "destructor" that you're not initializing things first and cleaning up afterwards. Variables often need to be initialized and cleaned-up, regardless of their scope. By default, the compiler clears out globals automatically for you by setting them to zero, but it's risky to depend on default behaviours like that from your environment. Constructors are simply methods used for INITIALIZING an object's state. They're usually just a bunch of assignment statements. They may also create other objects required inside of the object. Simple objects that don't inherit from a parent class with a constructor that needs to be called don't need constructors. Destructors are for cleaning up when you're finished using the object. For simple classes, they're often not necessary. But if you allocate anything from the heap within the class, then they're required if you want to avoid memory leaks. However, it's a good practice to always have constructors and destructors defined, even if they're empty. And if they ARE empty, the compiler optimizes them out. The nice thing about them is they're called automatically, unlike your existing code that needs to be called explicitly. So don't tell me you dislike creating things! Classes with constructors and destructors require less code than what you're doing now ... guaranteed. Even if you're working with a bunch global variables, any nontrivial application has code that initializes groups of related variables, and breaks them down when you're finished. One of the biggest hurdles functional programmers need to overcome when learning OOP is that all of those global variables need to be collected into closely-related groups, and those groups are called "classes". Then you move the code that's already there that's used to initialize those variables into the class' constructor, and any code that's already there for cleaning up into the class' destructor. Then you want to hide the variables themselves ("encapsulation") by defining properties so if the underlying implementations change, you don't have to change every single place they're referenced. Trust me ... after you've been using OOP principles correctly for a while, it becomes second-nature, and you'll wonder how you managed to keep your sanity dealing with everything splattered all over the place with no organization whatsoever. -
I did a CodeRage 9 talk on a topic that might help: https://www.youtube.com/watch?v=qqKx8fQTTfI
-
Is it really good practice to create Forms only as needed? Always?
David Schwartz replied to Mike Torrettinni's topic in VCL
Generally speaking, I don't auto-create any forms that aren't required to be around for the entire duration of the program's execution. So there's the Main form, any DataModules that are used by other forms (bad practice, but very common), and any other forms that are simply easier to leave around to hang onto values that would otherwise need to be saved somewhere else. Everything else is created as-needed and freed when I'm done using it. As far as your 50-tabbed notebook ... it must take forever to open that form in Delphi! At some point, I'd change that from a tabbed notebook to a panel where I loaded up an embedded form (created on-the-fly) from a combobox selection. -
Should my record be a class instead?
David Schwartz replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
It's unclear what your point is other than to formulate an excuse not to use classes. I mean, you're going through a lot of gyrations simply to avoid using classes. I think Uwe did you a disservice by simply wrapping your existing mess inside of a class. It would be far simpler to just convert it to a class directly so you could see the difference. Your PrepareGlobalRec method IS your constructor, so claiming you don't need "create" is baseless. constructor SomeName; <-- This is for doing exactly what Preparexxxx is doing! The name is irrelevant You probably will also need a destructor to avoid memory leaks, although if the lifetime of these vars is the same as the application, then they'll be freed when the program quits. I'm not sure why you think you need 100's of global vars other than this is just how you've been doing things for years. This is how VB structures things. There are generally very few global vars needed in properly designed Delphi apps. What you'd probably want to do is put them inside of a DataModule or Form and access them from there. Do you NEED TO? No, of course not. Nor do you need to use records or arrays for anything either, just lots and lots of variables. Either you're going to use OOP principles to build your software or you're not. It's up to you. You don't need to ask anybody here for permission to build something using pre-OOP practices. Sure, you CAN ... but why? -
General DB access question -- paging query results
David Schwartz replied to David Schwartz's topic in Databases
This has to do entirely with how CLOBs are stored vs. VARCHAR2's. The data is new -- it happens with five records created yesterday. And changing the architecture is not going to help because if you need to store >4k then you need to use LOBs. Period. Apparently Oracle saves CLOBs and BLOBs directly to disk in their own files, not in the DB. So it's way slower loading them. I don't know why other DBs don't seem to have this problem with huge text entities (like SQLServer's Memo fields). -
Include unused class and its RTTI in the executable
David Schwartz replied to Attila Kovacs's topic in General Help
is there any guarantee that the compiler won't ever figure out the never-true condition and simple ignore the code in the block? If it's never executed then there can't be any side-effects, so why not just ignore it entirely? That would not solve the problem, would it? I don't like stuff that depends on subtle compiler optimizations that can occur silently and I'd never know they happened unless I happened to look at the generated code. It's even worse than that because it would work fine in debug mode that does no optimization, but then fail when the code is built as a Release and optimization is turned on. -
Include unused class and its RTTI in the executable
David Schwartz replied to Attila Kovacs's topic in General Help
put this somewhere: TMyClass.Create().Free(); Or add a Register() method and call it in an Initialization section somewhere.