Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: 1 ... 719 720 [721] 722 723 ... 796

Author Topic: if self.isCoder(): post() #Programming Thread  (Read 890195 times)

Maximum Spin

  • Bay Watcher
  • [OPPOSED_TO_LIFE] [GOES_TO_ELEVEN]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10800 on: April 12, 2018, 02:17:15 am »

Generally, it's the idea that a "higher layer", ie the one that does something to something (that thing being the lower layer), should simply specify what it wants rather than meddle directly with ("depend on") the particular implementation of the lower layer, and then it's the lower layer's responsibility to provide what the higher layer asks for. The rationale for this is that you can thus apply the same higher layer to many different lower layers as long as they all follow the higher layer's rubric. To be honest, you're probably already doing this as it's a highly intuitive form of design.

I'll let someone else handle the specific examples of how to do it, though, I'm too much of an N.
Logged

wierd

  • Bay Watcher
  • I like to eat small children.
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10801 on: April 12, 2018, 02:29:30 am »

I always heard it called Modular Programming, but then again, I am old, and predate object oriented programming.

The idea was that your main logic calls modules that do things, and only keeps track of the variables/values it needs to do the higher level logic, keeping the nitty gritty inside the modules.  This way you could completely replace the module later, and not break the rest of the program.

This required you to be rigorous with your containment and implementation of your modules. (As opposed to the pejoratively named alternative, which was "Spaghetti Code.")

I have seen the same basic idea carried over into OOP, and then extended. To me, the main feature of OOP is the object hierarchy model, which basically treats things as if they were giant arrays with lots of disparate members. For some things, this is quite handy. For others, (especially for object classes that lack good documentation) it makes the object into a black box that rattles a little when you shake it.*

*I dont know how many times I have had to probe the shit out of an object's heirarchy and manually document its members and sub-members. (Granted, I was using the VBA interface for a CAD program, and I was probing undocumented objects not listed in the developer's documentation to learn how to make use of them. The programming interface treated all the CAD operations as objects, with all the options for the operations as members in their hierarchies. I needed to know what and where to poke to programatically alter CAD files for automation tasks.)

My first experiences with it (again, because I am old, and have seen the entire computer revolution from the 80s onwards) were in implementing actually sane programs in the various flavors of BASIC that were baked into really old computers. Your main routine could be very short and sweet, but call subroutines like they were modules, and pass variables as needed. Using GOSUB instead of GOTO saved you a great deal of problems-- GOTO principally used line numbers, and those could change as you modified programs, causing all kinds of chaos. (later incarnations let you use a label instead of a line number, but the result was still ugly.)
« Last Edit: April 12, 2018, 02:40:50 am by wierd »
Logged

Maximum Spin

  • Bay Watcher
  • [OPPOSED_TO_LIFE] [GOES_TO_ELEVEN]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10802 on: April 12, 2018, 02:35:45 am »

I always heard it called Modular Programming, but then again, I am old, and predate object oriented programming.
They still call it that; in fact, hardly anybody says "dependency inversion principle" outside of silly compsci people who like pretentious names for things. :P
Logged

hops

  • Bay Watcher
  • Secretary of Antifa
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10803 on: April 12, 2018, 03:45:07 am »

I always heard it called Modular Programming, but then again, I am old, and predate object oriented programming.
They still call it that; in fact, hardly anybody says "dependency inversion principle" outside of silly compsci people who like pretentious names for things. :P
Mnemonic tho.
Logged
she/her. (Pronouns vary over time.) The artist formerly known as Objective/Cinder.

One True Polycule with flame99 <3

Avatar by makowka

wierd

  • Bay Watcher
  • I like to eat small children.
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10804 on: April 12, 2018, 03:57:39 am »

If you ask me, it sounds like the start of a humorous acronym.

Dependency
Inverserion
Principle
(for)
Software
Hierarchy
Implementation
Technologies

Me, I prefer Modular Programming much better. :P
Logged

MorleyDev

  • Bay Watcher
  • "It is not enough for it to just work."
    • View Profile
    • MorleyDev
Re: if self.isCoder(): post() #Programming Thread
« Reply #10805 on: April 12, 2018, 04:31:35 am »

I look at it this way. If you directly new up and invoke some other class/function inside of a class, you are basically copying all the functionality of that class/function and saying "I do all of this too".

You're conceptually just copying the code from that class/function and pasting it there, and making it a core part of the specification for your new class and that you care about everything that class/function does. If you were writing tests for your class, you would need to duplicate all the tests for that class/function you are calling.

Usually you don't want to do this. You want to defer the specification, because your class doesn't actually need to care about the details. For example: you just want to "getUser" somehow not "getUserFromSqlDatabaseWithThisExactConnectionString", but if you directly new up the SqlUserRepository class then that's exactly what you are doing.

So if you instead make it an interface and inject it, you are deferring that specification until runtime when the implementation gets injected. It means you no longer need to care about the how of some tangentially related functionality, and you can focus on the details that matter.
« Last Edit: April 12, 2018, 04:36:02 am by MorleyDev »
Logged

LoSboccacc

  • Bay Watcher
  • Σὺν Ἀθηνᾷ καὶ χεῖρα κίνει
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10806 on: April 12, 2018, 04:52:40 am »

modular programming is essential to but is not dependency inversion.

dependency inversion is not a coding style but a service locator pattern.

you can have two modules happily requiring each other in order of dependency, or you can have the client module having the service module passed from an external code block, doing dependency inversion.
Logged

MorleyDev

  • Bay Watcher
  • "It is not enough for it to just work."
    • View Profile
    • MorleyDev
Re: if self.isCoder(): post() #Programming Thread
« Reply #10807 on: April 12, 2018, 05:04:13 am »

I'd be careful describing it as a service locator pattern, since the following is a completely valid way of injecting your services:
Code: [Select]
static void Main()
{
   var app = new Application(
      new UserService(
          new UserRepository(
               Configuration.ReadFrom("./appsettings.json")
          )
      )
   );
   app.Run();
}

You don't need an extra bit of code that 'resolves' the services for you, though programmers tend to use them (and some frameworks have it baked in, like ASP.NET Core). The important detail is that you've detached your implementations of those classes from each-other via abstractions, not that you're using a factory/service locator pattern.

so the UserService could look like:
Code: [Select]
public UserService : IUserService
{
  public UserService(IUserRepository userRepository)
  {
      this.userRepository = userRepository;
  }
  ...
}

The IUserService and IUserRepository abstractions are what get passed around and what other parts of the code depend on.

Modular Programming, on the other hand, is referring to the ability to separate code into separate modules that can be used together instead of having to do like C++ and #include things, so it's very different to Dependency Inversion which deals with the relationship between implementations and abstractions. Proper Modular Programming can't be done in C++ (yet), Dependency Inversion can be done in C++ today.

Quoth the wikipedia: https://en.wikipedia.org/wiki/Dependency_inversion_principle
Quote
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.
« Last Edit: April 12, 2018, 05:20:17 am by MorleyDev »
Logged

Maximum Spin

  • Bay Watcher
  • [OPPOSED_TO_LIFE] [GOES_TO_ELEVEN]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10808 on: April 12, 2018, 05:36:34 am »

Modular Programming, on the other hand, is referring to the ability to separate code into separate modules that can be used together instead of having to do like C++ and #include things, so it's very different to Dependency Inversion which deals with the relationship between implementations and abstractions. Proper Modular Programming can't be done in C++ (yet), Dependency Inversion can be done in C++ today.
That's not really how I see the phrase used, especially historically, actually? I think people usually use modular programming to describe a smaller-scale modularity which is reflected in that concept of "everything accesses everything else through an abstraction, so one thing can be applied to many other things without caring about the details". What you describe (which, frankly, I consider an antipattern) seems like more of the "silly compsci people" definition than what's actually used on the ground.
Logged

MorleyDev

  • Bay Watcher
  • "It is not enough for it to just work."
    • View Profile
    • MorleyDev
Re: if self.isCoder(): post() #Programming Thread
« Reply #10809 on: April 12, 2018, 05:42:34 am »

What you describe (which, frankly, I consider an antipattern) seems like more of the "silly compsci people" definition than what's actually used on the ground.

The usage has been conflated, terminology is confused. Modular Programming as a casual non-formal term just seems to refer to "splitting code off into discrete modules", which is...I mean, if you aren't just throwing everything into the main that's kinda what you're doing. So it's not that useful a term really, for me at least.

I mean yeah, if you want to call just specifically using abstractions "modular programming", sure why not. But formal terms exist to provide common language.

And Dependency Inversion isn't a silly compsci thing, since it comes out of the whole SOLID acronym and that actually doesn't tend to get taught at schools and was coined by Michael Feathers. Like, if you're uni is teaching you SOLID they're already ahead of the other unis. It's more of a silly Agile term :P

Now Liskov Substitution Principle, there's a needlessly silly term for :)
« Last Edit: April 12, 2018, 05:59:04 am by MorleyDev »
Logged

wierd

  • Bay Watcher
  • I like to eat small children.
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10810 on: April 12, 2018, 05:59:02 am »

Well, I mean, modular programming requires you to abstract what a module does as a mere function call.

EG

GetAllUsers($Table,Array());

All you need to know is that it needs a table, and an array to store data in. Exactly what it does and how it does it are not of consequence. The code that accepts that array just expects the array to be populated with something (and *should* have a conditional check to assure it is not null length), then runs with it.

"GetAllUsers" is a module. It could be an import, or a declared function, or even an inlined function-- but normally it is going to be a declared function or a library import.  As far as the main program is concerned, you can substitute it with any other implementation that returns a valid result using those two variables.

The C world deals with this with headers and primitives, which I guess you could call abstractions, but not all languages need those things. 

What I am getting at is that you have to use abstraction to use modular programming. They are inseparable.
Logged

MorleyDev

  • Bay Watcher
  • "It is not enough for it to just work."
    • View Profile
    • MorleyDev
Re: if self.isCoder(): post() #Programming Thread
« Reply #10811 on: April 12, 2018, 06:00:08 am »

What I am getting at is that you have to use abstraction to use modular programming. They are inseparable.

It's not an abstraction though, it's an encapsulation, either a high-level or low-level object as Dependency Inversion defines it. Again, you are abstracting in one of the definitions of that sure, but it's not an abstraction as the kind meant with Dependency Inversion or the kind meant when programmers typically talk about abstraction.

Yeah, you need to split your code into encapsulated modules to accomplish DIP but you can split your code into modular chunks and not follow dependency inversion principle. You're just calling new and invoking functions directly there, instead of through an abstraction layer.

Or to put it another way, if you were Unit Testing your above code, you would also need to have tests that cover the behaviour of GetAllUsers. If your unit tests have to include it's behaviour like that, it's an implementation and not an abstraction, your code is still tightly coupled to the implementation of GetAllUsers, and all your unit tests have become integration tests.

If it was an abstraction, you could mock/stub/fake it out, have unit tests that cover just the behaviour of the code but not GetAllUsers, and then have separate unit tests that cover GetAllUsers but don't cover your above code. All
« Last Edit: April 12, 2018, 06:36:55 am by MorleyDev »
Logged

wierd

  • Bay Watcher
  • I like to eat small children.
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10812 on: April 12, 2018, 06:36:41 am »

...

I test core program function all the time with mocked up functions.

eg, I might have something like this

Sub GetAllUsers($table,Array())
     Array(0)="First User"
     Array(1)="Second User"
     Return
End Sub

which simply puts two strings into the array, and ignores the table I just fed it. My main program can proceed with this without any questions, and I can focus on behavior of what I want to do with that array, then come back and deal with this function's implementation later.

Ideally, I would put some code to check the size of the passed array to make sure it is properly dimensioned, then redimension accordingly, then populate-- but meh, you get the idea.

To me, that is a mocked up function. *shrug*
Logged

MorleyDev

  • Bay Watcher
  • "It is not enough for it to just work."
    • View Profile
    • MorleyDev
Re: if self.isCoder(): post() #Programming Thread
« Reply #10813 on: April 12, 2018, 06:38:35 am »

That's...not a Unit Test though.

A unit test is a piece of code that you can run even after you've implemented GetAllUsers, and will run whatever wants to call GetAllUsers and make sure it behaves as expected. And then you keep and run all your unit tests whenever you make code changes, even after GetAllUsers has been implemented.

Even if GetAllUsers was wrongly implemented or not implemented, whatever uses GetAllUsers tests should still pass so long as that part of the code was implemented correctly, but the unit tests for GetAllUsers should fail.

A mock is a fake implementation you pass around and make return values as if it was GetAllUsers, and then in your unit tests you check what it was invoked with to ensure the contract of their interaction is honoured. That's a mock. (What you did there is a temporary stub implementation).

So your unit test of GetAllUsers would be code that runs to test that GetAllUsers behaves as expected, and then whatever calls it would also have a unit test which is code that checks if that behaves as expected but doesn't check if GetAllUsers behaves as expected, since that's not a part of the behaviour of that code and so isn't needed to be tested thanks to being separated by an abstraction layer.

So to pseudocode, a FindUser that depends on GetAllUsers could look like (I'm using function parameters to do the injection here):
Code: [Select]
(User | null) FindUser(userName, getAllUsers = GetAllUsers) {
    return getAllUsers().filter(user => user.name == userName).firstOrNull();
}

@unittest
void FindUserWithNoUsersReturnsNull()
{
     var mockGetAllUsers = createMock(GetAllUsers);
     mockGetAllUsers.Setup().Returns([]); // setup that getallusers will return empty

     var result = FindUser("anystring", mockGetAllUsers);

     mockGetAllUsers.Verify(); // verify getallusers was called
     Assert.IsNull(result);
}


@unittest
void FindUserWhereExistsReturnsUser()
{
     var expectedResult = { user: "exists" };
     var mockGetAllUsers = createMock(GetAllUsers);
     mockGetAllUsers.Setup().Returns([{ name: "exists" }, { name: "other" }]);

     var result = FindUser("exists", mockGetAllUsers);

     mockGetAllUsers.Verify(); // verify getallusers was called
     Assert.Equals(result, expectedResult);
}

@unittest
void FindUserWithOnlyOtherUsersReturnsNull()
{
     var mockGetAllUsers = createMock(GetAllUsers);
     mockGetAllUsers.Setup().Returns([{ name: "other1" }, { name: "other2" }]); // setup that getallusers will return only other users
     var result = FindUser("anystring", mockGetAllUsers );
     mockGetAllUsers.Verify(); // verify getallusers was called
     Assert.IsNull(result);
}


Simple example sure, but that's one way to do unit tests with functions and mocks. And then whenever you make a change to your code, you rerun your unit tests so if any of them break you know the functionality of that  part of the code has been altered and can either fix the tests if it's meant to behave like that, or fix the code if it's not.

You'd use the constructor to inject things in an OOP-only language (C#, Java, etc.), but the same basic principle applies.
« Last Edit: April 12, 2018, 07:40:59 am by MorleyDev »
Logged

bloop_bleep

  • Bay Watcher
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #10814 on: April 12, 2018, 12:35:26 pm »

I think what MorleyDev's trying to say is that DIP allows you to specify the low-level module implementation at runtime (e.g. by passing a function as a parameter), while using the usual form of modular programming makes this implementation fixed at compile-time. That is, using DIP you can specify different low-level modules for the same high-level module within the same execution of the program, but that isn't possible with other forms of modular programming.

Honestly, I see no point in using DIP in the majority of cases, since most of the time when you're writing a program the separate modules don't change that much if at all during execution.

And the code snippet that wierd showed us was most definitely an example of unit testing. Unit testing is simply a way of testing individual parts of the code instead of the whole program at once. Also, C++ certainly does have modular programming. All programming languages worth their salt do. Modular programming is a coding technique, not a feature; the document you linked was simply suggesting an alternative to the already present system (the former of which I personally disagree with, since they seem to be trying to solve problems that aren't there). The most commonly accepted method is by splitting your program into header and source files -- each module in the program has a header file, which contains its interface, and a source file, which contains its implementation. Whenever you would want to use another module, you would #include only its header file -- this is so that you can modify its implementation without having to rebuild everything else that depends upon this module. This is about as modular as you can get, and is entirely functional enough for the vast majority of cases. There is no reason to use more sophisticated mechanisms when simple copy-and-paste (combined with include guards) is sufficient.
Logged
Quote from: KittyTac
The closest thing Bay12 has to a flamewar is an argument over philosophy that slowly transitioned to an argument about quantum mechanics.
Quote from: thefriendlyhacker
The trick is to only make predictions semi-seriously.  That way, I don't have a 98% failure rate. I have a 98% sarcasm rate.
Pages: 1 ... 719 720 [721] 722 723 ... 796